pull/3539/head
Richard Harris 6 years ago
parent 8addd2313e
commit 0bcbe965dc

8
package-lock.json generated

@ -507,13 +507,13 @@
"dev": true "dev": true
}, },
"code-red": { "code-red": {
"version": "0.0.6", "version": "0.0.7",
"resolved": "https://registry.npmjs.org/code-red/-/code-red-0.0.6.tgz", "resolved": "https://registry.npmjs.org/code-red/-/code-red-0.0.7.tgz",
"integrity": "sha512-nsAPEQ+o1Z8UyIbOaOqfTxWHaA4JarPH7OBxDPmqAkkRvATvc2j7khlaAtsXnFreH2fTwekmZed4GlHvY3hQiw==", "integrity": "sha512-UgcXT2tiYOFe5MF6FYeP1s+XKvd5XFlm+p+By6/Q/0/NwgT2ZqkR32liLhosDivtO1vvtLTg51LN1LUW5AYSEw==",
"dev": true, "dev": true,
"requires": { "requires": {
"acorn": "^7.0.0", "acorn": "^7.0.0",
"astring": "github:Rich-Harris/astring#generic-handler", "astring": "github:Rich-Harris/astring#ff83f5e4e75b304cdd428ada4a71372276b0084d",
"estree-walker": "^0.6.1", "estree-walker": "^0.6.1",
"is-reference": "^1.1.3", "is-reference": "^1.1.3",
"periscopic": "^1.0.0", "periscopic": "^1.0.0",

@ -63,7 +63,7 @@
"acorn": "^7.0.0", "acorn": "^7.0.0",
"agadoo": "^1.0.1", "agadoo": "^1.0.1",
"c8": "^5.0.1", "c8": "^5.0.1",
"code-red": "0.0.6", "code-red": "0.0.7",
"codecov": "^3.5.0", "codecov": "^3.5.0",
"css-tree": "1.0.0-alpha22", "css-tree": "1.0.0-alpha22",
"eslint": "^6.3.0", "eslint": "^6.3.0",

@ -1,4 +1,4 @@
import MagicString, { Bundle } from 'magic-string'; import MagicString from 'magic-string';
// @ts-ignore // @ts-ignore
import { walk, childKeys } from 'estree-walker'; import { walk, childKeys } from 'estree-walker';
import { getLocator } from 'locate-character'; import { getLocator } from 'locate-character';
@ -333,8 +333,16 @@ export default class Component {
// } // }
// ); // );
const printed = print({ type: 'Program', body: result } as any); const program: any = { type: 'Program', body: result };
console.log(printed);
walk(program, {
enter: (node) => {
if (node.type === 'Identifier' && node.name[0] === '@') {
const alias = this.helper(node.name.slice(1));
node.name = alias.name;
}
}
});
const referenced_globals = Array.from( const referenced_globals = Array.from(
this.globals, this.globals,
@ -348,10 +356,10 @@ export default class Component {
alias, alias,
})); }));
const module = create_module( create_module(
printed.code, program,
format, format,
name.name, name,
banner, banner,
compile_options.sveltePath, compile_options.sveltePath,
imported_helpers, imported_helpers,
@ -362,61 +370,14 @@ export default class Component {
.map(variable => ({ .map(variable => ({
name: variable.name, name: variable.name,
as: variable.export_name, as: variable.export_name,
})), }))
this.source
); );
const parts = module.split('✂]');
const final_chunk = parts.pop();
const compiled = new Bundle({ separator: '' });
function add_string(str: string) {
compiled.addSource({
content: new MagicString(str),
});
}
const { filename } = compile_options;
// special case — the source file doesn't actually get used anywhere. we need
// to add an empty file to populate map.sources and map.sourcesContent
if (!parts.length) {
compiled.addSource({
filename,
content: new MagicString(this.source).remove(0, this.source.length),
});
}
const pattern = /\[✂(\d+)-(\d+)$/;
parts.forEach((str: string) => {
const chunk = str.replace(pattern, '');
if (chunk) add_string(chunk);
const match = pattern.exec(str);
const snippet = this.code.snip(+match[1], +match[2]);
compiled.addSource({
filename,
content: snippet,
});
});
add_string(final_chunk);
css = compile_options.customElement css = compile_options.customElement
? { code: null, map: null } ? { code: null, map: null }
: this.stylesheet.render(compile_options.cssOutputFilename, true); : this.stylesheet.render(compile_options.cssOutputFilename, true);
js = { js = print(program);
code: compiled.toString(),
map: compiled.generateMap({
includeContent: true,
file: compile_options.outputFilename,
}),
};
} }
return { return {

@ -1,7 +1,6 @@
import deindent from './utils/deindent';
import list from '../utils/list'; import list from '../utils/list';
import { ModuleFormat, Node, Identifier } from '../interfaces'; import { ModuleFormat, Node, Identifier } from '../interfaces';
import { stringify_props } from './utils/stringify_props'; import { b, x } from 'code-red';
const wrappers = { esm, cjs }; const wrappers = { esm, cjs };
@ -11,24 +10,23 @@ interface Export {
} }
export default function create_module( export default function create_module(
code: string, program: any,
format: ModuleFormat, format: ModuleFormat,
name: string, name: Identifier,
banner: string, banner: string,
sveltePath = 'svelte', sveltePath = 'svelte',
helpers: Array<{ name: string; alias: Identifier }>, helpers: Array<{ name: string; alias: Identifier }>,
globals: Array<{ name: string; alias: Identifier }>, globals: Array<{ name: string; alias: Identifier }>,
imports: Node[], imports: Node[],
module_exports: Export[], module_exports: Export[]
source: string ) {
): string {
const internal_path = `${sveltePath}/internal`; const internal_path = `${sveltePath}/internal`;
if (format === 'esm') { if (format === 'esm') {
return esm(code, name, banner, sveltePath, internal_path, helpers, globals, imports, module_exports, source); return esm(program, name, banner, sveltePath, internal_path, helpers, globals, imports, module_exports);
} }
if (format === 'cjs') return cjs(code, name, banner, sveltePath, internal_path, helpers, globals, imports, module_exports); if (format === 'cjs') return cjs(program, name, banner, sveltePath, internal_path, helpers, globals, imports, module_exports);
throw new Error(`options.format is invalid (must be ${list(Object.keys(wrappers))})`); throw new Error(`options.format is invalid (must be ${list(Object.keys(wrappers))})`);
} }
@ -40,54 +38,75 @@ function edit_source(source, sveltePath) {
} }
function esm( function esm(
code: string, program: any,
name: string, name: Identifier,
banner: string, _banner: string,
sveltePath: string, sveltePath: string,
internal_path: string, internal_path: string,
helpers: Array<{ name: string; alias: Identifier }>, helpers: Array<{ name: string; alias: Identifier }>,
globals: Array<{ name: string; alias: Identifier }>, globals: Array<{ name: string; alias: Identifier }>,
imports: Node[], imports: Node[],
module_exports: Export[], module_exports: Export[]
source: string
) { ) {
const internal_imports = helpers.length > 0 && ( const import_declaration = {
`import ${stringify_props(helpers.map(h => h.name === h.alias.name ? h.name : `${h.name} as ${h.alias}`).sort())} from ${JSON.stringify(internal_path)};` type: 'ImportDeclaration',
); specifiers: helpers.map(h => ({
const internal_globals = globals.length > 0 && ( type: 'ImportSpecifier',
`const ${stringify_props(globals.map(g => `${g.name}: ${g.alias}`).sort())} = ${helpers.find(({ name }) => name === 'globals').alias};` local: h.alias,
); imported: { type: 'Identifier', name: h.name }
})),
const user_imports = imports.length > 0 && ( source: { type: 'Literal', value: internal_path }
imports }
.map((declaration: Node) => {
const import_source = edit_source(declaration.source.value, sveltePath); const internal_globals = globals.length > 0 &&{
type: 'VariableDeclaration',
return ( kind: 'const',
source.slice(declaration.start, declaration.source.start) + declarations: [{
JSON.stringify(import_source) + id: {
source.slice(declaration.source.end, declaration.end) type: 'ObjectPattern',
); properties: globals.sort((a, b) => a.name < b.name ? -1 : 1).map(g => ({
}) type: 'Property',
.join('\n') method: false,
); shorthand: false,
computed: false,
return deindent` key: { type: 'Identifier', name: g.name },
${banner} value: { type: 'Identifier', name: g.alias }
${internal_imports} }))
},
init: { type: 'Identifier', name: helpers.find(({ name }) => name === 'globals').alias }
}]
};
// edit user imports
imports.forEach(node => {
node.source.value = edit_source(node.source.value, sveltePath);
});
const exports = module_exports.length > 0 && {
type: 'ExportNamedDeclaration',
specifiers: module_exports.map(x => ({
type: 'Specifier',
local: { type: 'Identifier', name: x.name },
exported: { type: 'Identifier', name: x.as }
}))
};
program.body = b`
${import_declaration}
${internal_globals} ${internal_globals}
${user_imports} ${imports}
${code} ${program.body}
export default ${name}; export default ${name};
${module_exports.length > 0 && `export { ${module_exports.map(e => e.name === e.as ? e.name : `${e.name} as ${e.as}`).join(', ')} };`}`; ${exports}
`;
} }
function cjs( function cjs(
code: string, program: any,
name: string, name: Identifier,
banner: string, _banner: string,
sveltePath: string, sveltePath: string,
internal_path: string, internal_path: string,
helpers: Array<{ name: string; alias: Identifier }>, helpers: Array<{ name: string; alias: Identifier }>,
@ -95,52 +114,80 @@ function cjs(
imports: Node[], imports: Node[],
module_exports: Export[] module_exports: Export[]
) { ) {
const declarations = helpers.map(h => `${h.alias.name === h.name ? h.name : `${h.name}: ${h.alias}`}`).sort(); const internal_requires = {
type: 'VariableDeclaration',
const internal_imports = helpers.length > 0 && ( kind: 'const',
`const ${stringify_props(declarations)} = require(${JSON.stringify(internal_path)});\n` declarations: [{
); type: 'VariableDeclarator',
const internal_globals = globals.length > 0 && ( id: {
`const ${stringify_props(globals.map(g => `${g.name}: ${g.alias}`).sort())} = ${helpers.find(({ name }) => name === 'globals').alias};` type: 'ObjectPattern',
); properties: helpers.sort((a, b) => a.name < b.name ? -1 : 1).map(h => ({
type: 'Property',
const requires = imports.map(node => { method: false,
let lhs; shorthand: false,
computed: false,
if (node.specifiers[0].type === 'ImportNamespaceSpecifier') { key: { type: 'Identifier', name: h.name },
lhs = node.specifiers[0].local.name; value: h.alias,
} else { kind: 'init'
const properties = node.specifiers.map(s => { }))
if (s.type === 'ImportDefaultSpecifier') { },
return `default: ${s.local.name}`; init: x`require("${internal_path}")`
} }]
};
return s.local.name === s.imported.name
? s.local.name const internal_globals = globals.length > 0 &&{
: `${s.imported.name}: ${s.local.name}`; type: 'VariableDeclaration',
}); kind: 'const',
declarations: [{
lhs = `{ ${properties.join(', ')} }`; type: 'VariableDeclarator',
} id: {
type: 'ObjectPattern',
const source = edit_source(node.source.value, sveltePath); properties: globals.sort((a, b) => a.name < b.name ? -1 : 1).map(g => ({
type: 'Property',
return `const ${lhs} = require("${source}");`; method: false,
}); shorthand: false,
computed: false,
const exports = [`exports.default = ${name};`].concat( key: { type: 'Identifier', name: g.name },
module_exports.map(x => `exports.${x.as} = ${x.name};`) value: { type: 'Identifier', name: g.alias }
); }))
},
return deindent` init: { type: 'Identifier', name: helpers.find(({ name }) => name === 'globals').alias }
${banner} }]
};
const user_requires = imports.map(node => ({
type: 'VariableDeclaration',
kind: 'const',
declarations: [{
type: 'VariableDeclarator',
id: node.specifiers[0].type === 'ImportNamespaceSpecifier'
? { type: 'Identifier', name: node.specifiers[0].local.name }
: {
type: 'ObjectPattern',
properties: node.specifiers.map(s => ({
type: 'Property',
method: false,
shorthand: false,
computed: false,
key: s.imported || { type: 'Identifier', name: 'default' },
value: s.local
}))
},
init: x`require("${edit_source(node.source.value, sveltePath)}")`
}]
}));
const exports = module_exports.map(x => b`exports.${{ type: 'Identifier', name: x.as }} = ${{ type: 'Identifier', name: x.name }};`);
program.body = b`
"use strict"; "use strict";
${internal_requires}
// ${internal_globals}
${user_requires}
${internal_imports} ${program.body}
${internal_globals}
${requires}
${code}
${exports}`; exports.default = ${name};
${exports}
`;
} }

@ -255,7 +255,7 @@ export default class Block {
const properties: Record<string, any> = {}; const properties: Record<string, any> = {};
const noop = x`noop`; const noop = x`@noop`;
properties.key = key properties.key = key
properties.first = this.first; properties.first = this.first;
@ -296,7 +296,7 @@ export default class Block {
properties.mount = noop; properties.mount = noop;
} else { } else {
properties.mount = x`function mount(#target, anchor) { properties.mount = x`function mount(#target, anchor) {
//${this.chunks.mount} ${this.chunks.mount}
}`; }`;
} }
@ -354,21 +354,22 @@ export default class Block {
} }
const return_value: any = x`{ const return_value: any = x`{
// key: ${properties.key}, key: ${properties.key},
// first: ${properties.first}, first: ${properties.first},
// c: ${properties.create}, c: ${properties.create},
// l: ${properties.claim}, l: ${properties.claim},
// h: ${properties.hydrate}, h: ${properties.hydrate},
m: ${properties.mount}, m: ${properties.mount},
// p: ${properties.update}, p: ${properties.update},
// r: ${properties.measure}, r: ${properties.measure},
// f: ${properties.fix}, f: ${properties.fix},
// a: ${properties.animate}, a: ${properties.animate},
// i: ${properties.intro}, i: ${properties.intro},
// o: ${properties.outro}, o: ${properties.outro},
// d: ${properties.destroy} d: ${properties.destroy}
}`; }`;
// TODO should code-red do this automatically? probably
return_value.properties = return_value.properties.filter(prop => prop.value); return_value.properties = return_value.properties.filter(prop => prop.value);
/* eslint-disable @typescript-eslint/indent,indent */ /* eslint-disable @typescript-eslint/indent,indent */

@ -390,7 +390,12 @@ export default function dom(
`); `);
} }
const prop_names = x`[${props.map(v => ({ type: 'Literal', value: v.export_name }))}]`; const prop_names = x`[]`;
// TODO find a more idiomatic way of doing this
props.forEach(v => {
(prop_names as any).elements.push({ type: 'Literal', value: v.export_name });
});
if (options.customElement) { if (options.customElement) {
body.push(b` body.push(b`
@ -453,5 +458,18 @@ export default function dom(
`); `);
} }
return body; return flatten(body, []);
}
function flatten(nodes: any[], target: any[]) {
for (let i = 0; i < nodes.length; i += 1) {
const node = nodes[i];
if (Array.isArray(node)) {
flatten(node, target);
} else {
target.push(node);
}
}
return target;
} }

@ -3,7 +3,7 @@ import * as path from "path";
import * as fs from "fs"; import * as fs from "fs";
import { rollup } from 'rollup'; import { rollup } from 'rollup';
import * as virtual from 'rollup-plugin-virtual'; import * as virtual from 'rollup-plugin-virtual';
import { clear_loops, flush, set_now, set_raf } from "../../internal"; import { clear_loops, flush, set_now, set_raf, component_subscribe } from "../../internal";
import { import {
showOutput, showOutput,
@ -33,9 +33,7 @@ describe("runtime", () => {
require.extensions[".svelte"] = function(module, filename) { require.extensions[".svelte"] = function(module, filename) {
const options = Object.assign({ const options = Object.assign({
filename, filename
format: 'cjs',
sveltePath
}, compileOptions); }, compileOptions);
const { js: { code } } = compile(fs.readFileSync(filename, "utf-8"), options); const { js: { code } } = compile(fs.readFileSync(filename, "utf-8"), options);
@ -72,6 +70,7 @@ describe("runtime", () => {
const cwd = path.resolve(`test/runtime/samples/${dir}`); const cwd = path.resolve(`test/runtime/samples/${dir}`);
compileOptions = config.compileOptions || {}; compileOptions = config.compileOptions || {};
compileOptions.format = 'cjs';
compileOptions.sveltePath = sveltePath; compileOptions.sveltePath = sveltePath;
compileOptions.hydratable = hydrate; compileOptions.hydratable = hydrate;
compileOptions.immutable = config.immutable; compileOptions.immutable = config.immutable;

@ -1,5 +1,6 @@
export default { export default {
solo: 1, solo: 1,
show: 1,
html: '<h1>Hello world!</h1>' html: '<h1>Hello world!</h1>'
}; };
Loading…
Cancel
Save