diff --git a/src/compiler/compile/nodes/InlineComponent.ts b/src/compiler/compile/nodes/InlineComponent.ts
index 9990a8302d..c78f434527 100644
--- a/src/compiler/compile/nodes/InlineComponent.ts
+++ b/src/compiler/compile/nodes/InlineComponent.ts
@@ -18,6 +18,7 @@ export default class InlineComponent extends Node {
bindings: Binding[] = [];
handlers: EventHandler[] = [];
lets: Let[] = [];
+ css_custom_properties: Attribute[] = [];
children: INode[];
scope: TemplateScope;
@@ -46,6 +47,10 @@ export default class InlineComponent extends Node {
});
case 'Attribute':
+ if (node.name.startsWith('--')) {
+ this.css_custom_properties.push(new Attribute(component, this, scope, node));
+ break;
+ }
// fallthrough
case 'Spread':
this.attributes.push(new Attribute(component, this, scope, node));
diff --git a/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts b/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts
index c6299321f4..2ea149a848 100644
--- a/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts
+++ b/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts
@@ -18,6 +18,7 @@ import { extract_names } from 'periscopic';
import mark_each_block_bindings from '../shared/mark_each_block_bindings';
import { string_to_member_expression } from '../../../utils/string_to_member_expression';
import SlotTemplate from '../../../nodes/SlotTemplate';
+import { is_head } from '../shared/is_head';
type SlotDefinition = { block: Block; scope: TemplateScope; get_context?: Node; get_changes?: Node };
@@ -63,6 +64,10 @@ export default class InlineComponentWrapper extends Wrapper {
}
});
+ this.node.css_custom_properties.forEach(attr => {
+ block.add_dependencies(attr.dependencies);
+ });
+
this.var = {
type: 'Identifier',
name: (
@@ -146,6 +151,12 @@ export default class InlineComponentWrapper extends Wrapper {
}
}
+ const has_css_custom_properties = this.node.css_custom_properties.length > 0;
+ const css_custom_properties_wrapper = has_css_custom_properties ? block.get_unique_name('div') : null;
+ if (has_css_custom_properties) {
+ block.add_variable(css_custom_properties_wrapper);
+ }
+
const initial_props = this.slots.size > 0
? [
p`$$slots: {
@@ -491,17 +502,64 @@ export default class InlineComponentWrapper extends Wrapper {
${munged_handlers}
`);
+ if (has_css_custom_properties) {
+ block.chunks.create.push(b`${css_custom_properties_wrapper} = @element("div");`);
+ block.chunks.hydrate.push(b`@set_style(${css_custom_properties_wrapper}, "display", "contents");`);
+ this.node.css_custom_properties.forEach(attr => {
+ const dependencies = attr.get_dependencies();
+ const should_cache = attr.should_cache();
+ const last = should_cache && block.get_unique_name(`${attr.name.replace(/[^a-zA-Z_$]/g, '_')}_last`);
+ if (should_cache) block.add_variable(last);
+ const value = attr.get_value(block);
+ const init = should_cache ? x`${last} = ${value}` : value;
+
+ block.chunks.hydrate.push(b`@set_style(${css_custom_properties_wrapper}, "${attr.name}", ${init});`);
+ if (dependencies.length > 0) {
+ let condition = block.renderer.dirty(dependencies);
+ if (should_cache) condition = x`${condition} && (${last} !== (${last} = ${value}))`;
+
+ block.chunks.update.push(b`
+ if (${condition}) {
+ @set_style(${css_custom_properties_wrapper}, "${attr.name}", ${should_cache ? last : value});
+ }
+ `);
+ }
+ });
+ }
block.chunks.create.push(b`@create_component(${name}.$$.fragment);`);
if (parent_nodes && this.renderer.options.hydratable) {
+ let nodes = parent_nodes;
+ if (has_css_custom_properties) {
+ nodes = block.get_unique_name(`${css_custom_properties_wrapper.name}_nodes`);
+ block.chunks.claim.push(b`
+ ${css_custom_properties_wrapper} = @claim_element(${parent_nodes}, "DIV", { style: true })
+ var ${nodes} = @children(${css_custom_properties_wrapper});
+ `);
+ }
block.chunks.claim.push(
- b`@claim_component(${name}.$$.fragment, ${parent_nodes});`
+ b`@claim_component(${name}.$$.fragment, ${nodes});`
);
}
- block.chunks.mount.push(
- b`@mount_component(${name}, ${parent_node || '#target'}, ${parent_node ? 'null' : '#anchor'});`
- );
+ if (has_css_custom_properties) {
+ if (parent_node) {
+ block.chunks.mount.push(b`@append(${parent_node}, ${css_custom_properties_wrapper})`);
+ if (is_head(parent_node)) {
+ block.chunks.destroy.push(b`@detach(${css_custom_properties_wrapper});`);
+ }
+ } else {
+ block.chunks.mount.push(b`@insert(#target, ${css_custom_properties_wrapper}, #anchor);`);
+ // TODO we eventually need to consider what happens to elements
+ // that belong to the same outgroup as an outroing element...
+ block.chunks.destroy.push(b`if (detaching) @detach(${css_custom_properties_wrapper});`);
+ }
+ block.chunks.mount.push(b`@mount_component(${name}, ${css_custom_properties_wrapper}, null);`);
+ } else {
+ block.chunks.mount.push(
+ b`@mount_component(${name}, ${parent_node || '#target'}, ${parent_node ? 'null' : '#anchor'});`
+ );
+ }
block.chunks.intro.push(b`
@transition_in(${name}.$$.fragment, #local);
diff --git a/src/compiler/compile/render_ssr/handlers/InlineComponent.ts b/src/compiler/compile/render_ssr/handlers/InlineComponent.ts
index c4116e6591..2b3ca5fbcb 100644
--- a/src/compiler/compile/render_ssr/handlers/InlineComponent.ts
+++ b/src/compiler/compile/render_ssr/handlers/InlineComponent.ts
@@ -1,4 +1,5 @@
import { string_literal } from '../../utils/stringify';
+import { get_attribute_value } from './shared/get_attribute_value';
import Renderer, { RenderOptions } from '../Renderer';
import InlineComponent from '../../nodes/InlineComponent';
import { p, x } from 'code-red';
@@ -86,5 +87,19 @@ export default function(node: InlineComponent, renderer: Renderer, options: Rend
${slot_fns}
}`;
+ if (node.css_custom_properties.length > 0) {
+ renderer.add_string('
');
+ }
+
renderer.add_expression(x`@validate_component(${expression}, "${node.name}").$$render($$result, ${props}, ${bindings}, ${slots})`);
+
+ if (node.css_custom_properties.length > 0) {
+ renderer.add_string('
');
+ }
}
diff --git a/test/helpers.ts b/test/helpers.ts
index 78f000c274..4de53e6cf9 100644
--- a/test/helpers.ts
+++ b/test/helpers.ts
@@ -260,3 +260,11 @@ export function mkdirp(dir) {
// do nothing
}
}
+
+export function prettyPrintPuppeteerAssertionError(message) {
+ const match = /Error: Expected "(.+)" to equal "(.+)"/.exec(message);
+
+ if (match) {
+ assert.equal(match[1], match[2]);
+ }
+}
diff --git a/test/runtime-puppeteer/assert.js b/test/runtime-puppeteer/assert.js
new file mode 100644
index 0000000000..4ee8d9dda0
--- /dev/null
+++ b/test/runtime-puppeteer/assert.js
@@ -0,0 +1,54 @@
+export function deepEqual(a, b, message) {
+ if (!is_equal(a, b)) {
+ throw new Error(message || `Expected ${JSON.stringify(a)} to equal ${JSON.stringify(b)}`);
+ }
+}
+
+function is_equal(a, b) {
+ if (a && typeof a === 'object') {
+ const is_array = Array.isArray(a);
+ if (Array.isArray(b) !== is_array) return false;
+
+ if (is_array) {
+ if (a.length !== b.length) return false;
+ return a.every((value, i) => is_equal(value, b[i]));
+ }
+
+ const a_keys = Object.keys(a).sort();
+ const b_keys = Object.keys(b).sort();
+ if (a_keys.join(',') !== b_keys.join(',')) return false;
+
+ return a_keys.every(key => is_equal(a[key], b[key]));
+ }
+
+ return a === b;
+}
+
+export function equal(a, b, message) {
+ if (a != b) throw new Error(message || `Expected ${a} to equal ${b}`);
+}
+
+export function ok(condition, message) {
+ if (!condition) throw new Error(message || `Expected ${condition} to be truthy`);
+}
+
+export function htmlEqual(actual, expected, message) {
+ return deepEqual(
+ normalizeHtml(window, actual),
+ normalizeHtml(window, expected),
+ message
+ );
+}
+
+function normalizeHtml(window, html) {
+ try {
+ const node = window.document.createElement('div');
+ node.innerHTML = html
+ .replace(//g, '')
+ .replace(/>[\s\r\n]+<')
+ .trim();
+ return node.innerHTML.replace(/<\/?noscript\/?>/g, '');
+ } catch (err) {
+ throw new Error(`Failed to normalize HTML:\n${html}`);
+ }
+}
diff --git a/test/runtime-puppeteer/index.ts b/test/runtime-puppeteer/index.ts
new file mode 100644
index 0000000000..b700d47945
--- /dev/null
+++ b/test/runtime-puppeteer/index.ts
@@ -0,0 +1,250 @@
+import * as path from 'path';
+import * as fs from 'fs';
+import * as http from 'http';
+import { rollup } from 'rollup';
+import virtual from '@rollup/plugin-virtual';
+import puppeteer from 'puppeteer';
+
+import {
+ loadConfig,
+ loadSvelte,
+ mkdirp,
+ prettyPrintPuppeteerAssertionError
+} from '../helpers';
+import { deepEqual } from 'assert';
+
+const page = `
+
+
+
+
+`;
+
+let svelte;
+let server;
+let code;
+let browser;
+
+const internal = path.resolve('internal/index.mjs');
+const index = path.resolve('index.mjs');
+
+function create_server() {
+ return new Promise((fulfil, reject) => {
+ const server = http.createServer((req, res) => {
+ if (req.url === '/') {
+ res.end(page);
+ }
+
+ if (req.url === '/bundle.js') {
+ res.end(code);
+ }
+ });
+
+ server.on('error', reject);
+
+ server.listen('6789', () => {
+ fulfil(server);
+ });
+ });
+}
+
+const assert = fs.readFileSync(`${__dirname}/assert.js`, 'utf-8');
+
+describe('runtime (puppeteer)', () => {
+ before(async () => {
+ svelte = loadSvelte(false);
+ console.log('[runtime-puppeteer] Loaded Svelte');
+ server = await create_server();
+ console.log('[runtime-puppeteer] Started server');
+ browser = await puppeteer.launch();
+ console.log('[runtime-puppeteer] Launched puppeteer browser');
+ });
+
+ after(async () => {
+ if (server) server.close();
+ if (browser) await browser.close();
+ });
+
+ const failed = new Set();
+
+ function runTest(dir, hydrate) {
+ if (dir[0] === '.') return;
+
+ const config = loadConfig(`${__dirname}/samples/${dir}/_config.js`);
+ const solo = config.solo || /\.solo/.test(dir);
+ const skip = config.skip || /\.skip/.test(dir);
+
+ if (hydrate && config.skip_if_hydrate) return;
+
+ if (solo && process.env.CI) {
+ throw new Error('Forgot to remove `solo: true` from test');
+ }
+
+ (skip ? it.skip : solo ? it.only : it)(`${dir} ${hydrate ? '(with hydration)' : ''}`, async () => {
+ if (failed.has(dir)) {
+ // this makes debugging easier, by only printing compiled output once
+ throw new Error('skipping test, already failed');
+ }
+
+ const warnings = [];
+
+ const bundle = await rollup({
+ input: 'main',
+ plugins: [
+ {
+ name: 'testing-runtime-puppeteer',
+ resolveId(importee) {
+ if (importee === 'svelte/internal' || importee === './internal') {
+ return internal;
+ }
+
+ if (importee === 'svelte') {
+ return index;
+ }
+
+ if (importee === 'main') {
+ return 'main';
+ }
+ },
+ load(id) {
+ if (id === 'main') {
+ return `
+ import SvelteComponent from ${JSON.stringify(path.join(__dirname, 'samples', dir, 'main.svelte'))};
+ import config from ${JSON.stringify(path.join(__dirname, 'samples', dir, '_config.js'))};
+ import * as assert from 'assert';
+
+ export default async function (target) {
+ let unhandled_rejection = false;
+ function unhandled_rejection_handler(event) {
+ unhandled_rejection = event.reason;
+ }
+ window.addEventListener('unhandledrejection', unhandled_rejection_handler);
+
+ try {
+ if (config.before_test) config.before_test();
+
+ const options = Object.assign({}, {
+ target,
+ hydrate: ${String(!!hydrate)},
+ props: config.props,
+ intro: config.intro
+ }, config.options || {});
+
+ const component = new SvelteComponent(options);
+
+ if (config.html) {
+ assert.htmlEqual(target.innerHTML, config.html);
+ }
+
+ if (config.test) {
+ await config.test({
+ assert,
+ component,
+ target,
+ window,
+ });
+
+ component.$destroy();
+
+ if (unhandled_rejection) {
+ throw unhandled_rejection;
+ }
+ } else {
+ component.$destroy();
+ assert.htmlEqual(target.innerHTML, '');
+
+ if (unhandled_rejection) {
+ throw unhandled_rejection;
+ }
+ }
+
+ if (config.after_test) config.after_test();
+ } catch (error) {
+ if (config.error) {
+ assert.equal(err.message, config.error);
+ } else {
+ throw error;
+ }
+ } finally {
+ window.removeEventListener('unhandledrejection', unhandled_rejection_handler);
+ }
+ }
+ `;
+ }
+ return null;
+ },
+ transform(code, id) {
+ if (id.endsWith('.svelte')) {
+ const compiled = svelte.compile(code.replace(/\r/g, ''), {
+ ...config.compileOptions,
+ hydratable: hydrate,
+ immutable: config.immutable,
+ accessors: 'accessors' in config ? config.accessors : true
+ });
+
+ const out_dir = `${__dirname}/samples/${dir}/_output/${hydrate ? 'hydratable' : 'normal'}`;
+ const out = `${out_dir}/${path.basename(id).replace(/\.svelte$/, '.js')}`;
+
+ if (fs.existsSync(out)) {
+ fs.unlinkSync(out);
+ }
+
+ mkdirp(out_dir);
+ fs.writeFileSync(out, compiled.js.code, 'utf8');
+
+ compiled.warnings.forEach(w => warnings.push(w));
+
+ return compiled.js;
+ }
+ }
+ },
+ virtual({ assert })
+ ]
+ });
+
+ const result = await bundle.generate({ format: 'iife', name: 'test' });
+ code = result.output[0].code;
+
+ const page = await browser.newPage();
+
+ page.on('console', (type) => {
+ console[type._type](type._text);
+ });
+
+ page.on('error', error => {
+ console.log('>>> an error happened');
+ console.error(error);
+ });
+
+ try {
+ await page.goto('http://localhost:6789');
+
+ const result = await page.evaluate(() => test(document.querySelector('main')));
+ if (result) console.log(result);
+ } catch (err) {
+ failed.add(dir);
+ prettyPrintPuppeteerAssertionError(err.message);
+ throw err;
+ } finally {
+ if (config.warnings) {
+ deepEqual(warnings.map(w => ({
+ code: w.code,
+ message: w.message,
+ pos: w.pos,
+ start: w.start,
+ end: w.end
+ })), config.warnings);
+ } else if (warnings.length) {
+ failed.add(dir);
+ /* eslint-disable no-unsafe-finally */
+ throw new Error('Received unexpected warnings');
+ }
+ }
+ });
+ }
+
+ fs.readdirSync(`${__dirname}/samples`).forEach(dir => {
+ runTest(dir, false);
+ runTest(dir, true);
+ });
+});
diff --git a/test/runtime-puppeteer/samples/component-css-custom-properties-dynamic/Slider.svelte b/test/runtime-puppeteer/samples/component-css-custom-properties-dynamic/Slider.svelte
new file mode 100644
index 0000000000..4863966f85
--- /dev/null
+++ b/test/runtime-puppeteer/samples/component-css-custom-properties-dynamic/Slider.svelte
@@ -0,0 +1,17 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/test/runtime-puppeteer/samples/component-css-custom-properties-dynamic/_config.js b/test/runtime-puppeteer/samples/component-css-custom-properties-dynamic/_config.js
new file mode 100644
index 0000000000..96965c223b
--- /dev/null
+++ b/test/runtime-puppeteer/samples/component-css-custom-properties-dynamic/_config.js
@@ -0,0 +1,41 @@
+export default {
+ props: {
+ railColor1: 'black',
+ trackColor1: 'red',
+ railColor2: 'green',
+ trackColor2: 'blue'
+ },
+ html: `
+
+
+ `,
+ test({ component, assert, target }) {
+ component.railColor1 = 'yellow';
+ component.trackColor2 = 'orange';
+
+ assert.htmlEqual(target.innerHTML, `
+
+
+ `);
+ }
+};
diff --git a/test/runtime-puppeteer/samples/component-css-custom-properties-dynamic/main.svelte b/test/runtime-puppeteer/samples/component-css-custom-properties-dynamic/main.svelte
new file mode 100644
index 0000000000..94e68e38a2
--- /dev/null
+++ b/test/runtime-puppeteer/samples/component-css-custom-properties-dynamic/main.svelte
@@ -0,0 +1,23 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/test/runtime-puppeteer/samples/component-css-custom-properties/Slider.svelte b/test/runtime-puppeteer/samples/component-css-custom-properties/Slider.svelte
new file mode 100644
index 0000000000..4863966f85
--- /dev/null
+++ b/test/runtime-puppeteer/samples/component-css-custom-properties/Slider.svelte
@@ -0,0 +1,17 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/test/runtime-puppeteer/samples/component-css-custom-properties/_config.js b/test/runtime-puppeteer/samples/component-css-custom-properties/_config.js
new file mode 100644
index 0000000000..9c3b7cbdea
--- /dev/null
+++ b/test/runtime-puppeteer/samples/component-css-custom-properties/_config.js
@@ -0,0 +1,27 @@
+export default {
+ html: `
+
+
+ `,
+ test({ target, window, assert }) {
+ const railColor1 = target.querySelector('#slider-1 p');
+ const trackColor1 = target.querySelector('#slider-1 span');
+ const railColor2 = target.querySelector('#slider-2 p');
+ const trackColor2 = target.querySelector('#slider-2 span');
+
+ assert.htmlEqual(window.getComputedStyle(railColor1).color, 'rgb(0, 0, 0)');
+ assert.htmlEqual(window.getComputedStyle(trackColor1).color, 'rgb(255, 0, 0)');
+ assert.htmlEqual(window.getComputedStyle(railColor2).color, 'rgb(0, 255, 0)');
+ assert.htmlEqual(window.getComputedStyle(trackColor2).color, 'rgb(0, 0, 255)');
+ }
+};
diff --git a/test/runtime-puppeteer/samples/component-css-custom-properties/main.svelte b/test/runtime-puppeteer/samples/component-css-custom-properties/main.svelte
new file mode 100644
index 0000000000..0d92b7ae87
--- /dev/null
+++ b/test/runtime-puppeteer/samples/component-css-custom-properties/main.svelte
@@ -0,0 +1,15 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/test/server-side-rendering/index.ts b/test/server-side-rendering/index.ts
index e81058181d..f455cecc72 100644
--- a/test/server-side-rendering/index.ts
+++ b/test/server-side-rendering/index.ts
@@ -135,100 +135,105 @@ describe('ssr', () => {
});
// duplicate client-side tests, as far as possible
- fs.readdirSync('test/runtime/samples').forEach(dir => {
- if (dir[0] === '.') return;
+ runRuntimeSamples('runtime');
+ runRuntimeSamples('runtime-puppeteer');
- const config = loadConfig(`./runtime/samples/${dir}/_config.js`);
- const solo = config.solo || /\.solo/.test(dir);
+ function runRuntimeSamples(suite) {
+ fs.readdirSync(`test/${suite}/samples`).forEach(dir => {
+ if (dir[0] === '.') return;
- if (solo && process.env.CI) {
- throw new Error('Forgot to remove `solo: true` from test');
- }
+ const config = loadConfig(`./${suite}/samples/${dir}/_config.js`);
+ const solo = config.solo || /\.solo/.test(dir);
- if (config.skip_if_ssr) return;
+ if (solo && process.env.CI) {
+ throw new Error('Forgot to remove `solo: true` from test');
+ }
- (config.skip ? it.skip : solo ? it.only : it)(dir, () => {
- const cwd = path.resolve('test/runtime/samples', dir);
+ if (config.skip_if_ssr) return;
- cleanRequireCache();
+ (config.skip ? it.skip : solo ? it.only : it)(dir, () => {
+ const cwd = path.resolve(`test/${suite}/samples`, dir);
- delete global.window;
+ cleanRequireCache();
- const compileOptions = {
- sveltePath,
- ...config.compileOptions,
- generate: 'ssr',
- format: 'cjs'
- };
-
- require('../../register')(compileOptions);
+ delete global.window;
- glob('**/*.svelte', { cwd }).forEach(file => {
- if (file[0] === '_') return;
+ const compileOptions = {
+ sveltePath,
+ ...config.compileOptions,
+ generate: 'ssr',
+ format: 'cjs'
+ };
- const dir = `${cwd}/_output/ssr`;
- const out = `${dir}/${file.replace(/\.svelte$/, '.js')}`;
+ require('../../register')(compileOptions);
- if (fs.existsSync(out)) {
- fs.unlinkSync(out);
- }
+ glob('**/*.svelte', { cwd }).forEach(file => {
+ if (file[0] === '_') return;
- mkdirp(dir);
+ const dir = `${cwd}/_output/ssr`;
+ const out = `${dir}/${file.replace(/\.svelte$/, '.js')}`;
- try {
- const { js } = compile(
- fs.readFileSync(`${cwd}/${file}`, 'utf-8'),
- {
- ...compileOptions,
- filename: file
- }
- );
+ if (fs.existsSync(out)) {
+ fs.unlinkSync(out);
+ }
- fs.writeFileSync(out, js.code);
- } catch (err) {
- // do nothing
- }
- });
+ mkdirp(dir);
- try {
- if (config.before_test) config.before_test();
+ try {
+ const { js } = compile(
+ fs.readFileSync(`${cwd}/${file}`, 'utf-8'),
+ {
+ ...compileOptions,
+ filename: file
+ }
+ );
- const Component = require(`../runtime/samples/${dir}/main.svelte`).default;
- const { html } = Component.render(config.props, {
- store: (config.store !== true) && config.store
+ fs.writeFileSync(out, js.code);
+ } catch (err) {
+ // do nothing
+ }
});
- if (config.ssrHtml) {
- assert.htmlEqual(html, config.ssrHtml);
- } else if (config.html) {
- assert.htmlEqual(html, config.html);
- }
+ try {
+ if (config.before_test) config.before_test();
- if (config.test_ssr) {
- config.test_ssr({ assert });
- }
+ const Component = require(`../${suite}/samples/${dir}/main.svelte`).default;
+ const { html } = Component.render(config.props, {
+ store: (config.store !== true) && config.store
+ });
- if (config.after_test) config.after_test();
+ if (config.ssrHtml) {
+ assert.htmlEqual(html, config.ssrHtml);
+ } else if (config.html) {
+ assert.htmlEqual(html, config.html);
+ }
- if (config.show) {
- showOutput(cwd, compileOptions);
- }
- } catch (err) {
- err.stack += `\n\ncmd-click: ${path.relative(process.cwd(), cwd)}/main.svelte`;
+ if (config.test_ssr) {
+ config.test_ssr({ assert });
+ }
+
+ if (config.after_test) config.after_test();
- if (config.error) {
- if (typeof config.error === 'function') {
- config.error(assert, err);
+ if (config.show) {
+ showOutput(cwd, compileOptions);
+ }
+ } catch (err) {
+ err.stack += `\n\ncmd-click: ${path.relative(process.cwd(), cwd)}/main.svelte`;
+
+ if (config.error) {
+ if (typeof config.error === 'function') {
+ config.error(assert, err);
+ } else {
+ assert.equal(err.message, config.error);
+ }
} else {
- assert.equal(err.message, config.error);
+ showOutput(cwd, compileOptions);
+ throw err;
}
- } else {
- showOutput(cwd, compileOptions);
- throw err;
+ } finally {
+ set_current_component(null);
}
- } finally {
- set_current_component(null);
- }
+ });
});
- });
+ }
});