reshape ast

pull/2011/head
Richard Harris 7 years ago
parent 9f800fb914
commit af6167a998

@ -56,9 +56,6 @@ export default class Component {
namespace: string; namespace: string;
tag: string; tag: string;
instance_script: Node;
module_script: Node;
imports: Node[] = []; imports: Node[] = [];
module_javascript: string; module_javascript: string;
javascript: string; javascript: string;
@ -122,26 +119,6 @@ export default class Component {
this.stylesheet = new Stylesheet(source, ast, options.filename, options.dev); this.stylesheet = new Stylesheet(source, ast, options.filename, options.dev);
this.stylesheet.validate(this); this.stylesheet.validate(this);
const module_scripts = ast.js.filter(script => this.get_context(script) === 'module');
const instance_scripts = ast.js.filter(script => this.get_context(script) === 'default');
if (module_scripts.length > 1) {
this.error(module_scripts[1], {
code: `invalid-script`,
message: `A component can only have one <script context="module"> element`
});
}
if (instance_scripts.length > 1) {
this.error(instance_scripts[1], {
code: `invalid-script`,
message: `A component can only have one instance-level <script> element`
});
}
this.module_script = module_scripts[0];
this.instance_script = instance_scripts[0];
this.meta = process_meta(this, this.ast.html.children); this.meta = process_meta(this, this.ast.html.children);
this.namespace = namespaces[this.meta.namespace] || this.meta.namespace; this.namespace = namespaces[this.meta.namespace] || this.meta.namespace;
@ -170,7 +147,7 @@ export default class Component {
this.stylesheet.warnOnUnusedSelectors(stats); this.stylesheet.warnOnUnusedSelectors(stats);
if (!this.instance_script) { if (!ast.instance && !ast.module) {
const props = [...this.template_references]; const props = [...this.template_references];
this.declarations.push(...props); this.declarations.push(...props);
addToSet(this.mutable_props, this.template_references); addToSet(this.mutable_props, this.template_references);
@ -491,7 +468,7 @@ export default class Component {
} }
walk_module_js() { walk_module_js() {
const script = this.module_script; const script = this.ast.module;
if (!script) return; if (!script) return;
this.addSourcemapLocations(script.content); this.addSourcemapLocations(script.content);
@ -514,7 +491,7 @@ export default class Component {
} }
walk_instance_js_pre_template() { walk_instance_js_pre_template() {
const script = this.instance_script; const script = this.ast.instance;
if (!script) return; if (!script) return;
this.addSourcemapLocations(script.content); this.addSourcemapLocations(script.content);
@ -551,7 +528,7 @@ export default class Component {
} }
walk_instance_js_post_template() { walk_instance_js_post_template() {
const script = this.instance_script; const script = this.ast.instance;
if (!script) return; if (!script) return;
this.hoist_instance_declarations(); this.hoist_instance_declarations();
@ -565,7 +542,7 @@ export default class Component {
const component = this; const component = this;
let { instance_scope: scope, instance_scope_map: map } = this; let { instance_scope: scope, instance_scope_map: map } = this;
walk(this.instance_script.content, { walk(this.ast.instance.content, {
enter(node, parent) { enter(node, parent) {
let names; let names;
if (map.has(node)) { if (map.has(node)) {
@ -595,7 +572,7 @@ export default class Component {
const component = this; const component = this;
let { instance_scope: scope, instance_scope_map: map } = this; let { instance_scope: scope, instance_scope_map: map } = this;
walk(this.instance_script.content, { walk(this.ast.instance.content, {
enter(node, parent) { enter(node, parent) {
if (map.has(node)) { if (map.has(node)) {
scope = map.get(node); scope = map.get(node);
@ -637,7 +614,7 @@ export default class Component {
const coalesced_declarations = []; const coalesced_declarations = [];
let current_group; let current_group;
walk(this.instance_script.content, { walk(this.ast.instance.content, {
enter(node, parent) { enter(node, parent) {
if (/Function/.test(node.type)) { if (/Function/.test(node.type)) {
current_group = null; current_group = null;
@ -769,7 +746,7 @@ export default class Component {
const top_level_function_declarations = new Map(); const top_level_function_declarations = new Map();
this.instance_script.content.body.forEach(node => { this.ast.instance.content.body.forEach(node => {
if (node.type === 'VariableDeclaration') { if (node.type === 'VariableDeclaration') {
if (node.declarations.every(d => d.init && d.init.type === 'Literal' && !this.mutable_props.has(d.id.name) && !template_scope.containsMutable([d.id.name]))) { if (node.declarations.every(d => d.init && d.init.type === 'Literal' && !this.mutable_props.has(d.id.name) && !template_scope.containsMutable([d.id.name]))) {
node.declarations.forEach(d => { node.declarations.forEach(d => {
@ -875,7 +852,7 @@ export default class Component {
const unsorted_reactive_declarations = []; const unsorted_reactive_declarations = [];
this.instance_script.content.body.forEach(node => { this.ast.instance.content.body.forEach(node => {
if (node.type === 'LabeledStatement' && node.label.name === '$') { if (node.type === 'LabeledStatement' && node.label.name === '$') {
this.reactive_declaration_nodes.add(node); this.reactive_declaration_nodes.add(node);
@ -998,7 +975,7 @@ export default class Component {
this.has_reactive_assignments = true; this.has_reactive_assignments = true;
} }
if (allow_implicit && !this.instance_script) return; if (allow_implicit && !this.ast.instance && !this.ast.module) return;
if (this.instance_scope && this.instance_scope.declarations.has(name)) return; if (this.instance_scope && this.instance_scope.declarations.has(name)) return;
if (this.module_scope && this.module_scope.declarations.has(name)) return; if (this.module_scope && this.module_scope.declarations.has(name)) return;
if (template_scope && template_scope.names.has(name)) return; if (template_scope && template_scope.names.has(name)) return;
@ -1009,29 +986,6 @@ export default class Component {
message: `'${name}' is not defined` message: `'${name}' is not defined`
}); });
} }
get_context(script) {
const context = script.attributes.find(attribute => attribute.name === 'context');
if (!context) return 'default';
if (context.value.length !== 1 || context.value[0].type !== 'Text') {
this.error(script, {
code: 'invalid-script',
message: `context attribute must be static`
});
}
const value = context.value[0].data;
if (value !== 'module') {
this.error(context, {
code: `invalid-script`,
message: `If the context attribute is supplied, its value must be "module"`
});
}
return value;
}
} }
function process_meta(component, nodes) { function process_meta(component, nodes) {

@ -265,15 +265,15 @@ export default class Stylesheet {
this.nodesWithCssClass = new Set(); this.nodesWithCssClass = new Set();
this.nodesWithRefCssClass = new Map(); this.nodesWithRefCssClass = new Map();
if (ast.css[0] && ast.css[0].children.length) { if (ast.css && ast.css.children.length) {
this.id = `svelte-${hash(ast.css[0].content.styles)}`; this.id = `svelte-${hash(ast.css.content.styles)}`;
this.hasStyles = true; this.hasStyles = true;
const stack: (Rule | Atrule)[] = []; const stack: (Rule | Atrule)[] = [];
let currentAtrule: Atrule = null; let currentAtrule: Atrule = null;
walk(ast.css[0], { walk(ast.css, {
enter: (node: Node) => { enter: (node: Node) => {
if (node.type === 'Atrule') { if (node.type === 'Atrule') {
const last = stack[stack.length - 1]; const last = stack[stack.length - 1];

@ -112,7 +112,7 @@ export default class Expression {
// conditions — it doesn't apply if the dependency is inside a // conditions — it doesn't apply if the dependency is inside a
// function, and it only applies if the dependency is writable // function, and it only applies if the dependency is writable
// or a sub-path of a non-writable // or a sub-path of a non-writable
if (component.instance_script) { if (component.ast.instance) {
const owner = template_scope.getOwner(name); const owner = template_scope.getOwner(name);
const is_let = owner && (owner.type === 'InlineComponent' || owner.type === 'Element'); const is_let = owner && (owner.type === 'InlineComponent' || owner.type === 'Element');

@ -144,13 +144,13 @@ export default function dom(
} }
// instrument assignments // instrument assignments
if (component.instance_script) { if (component.ast.instance) {
let scope = component.instance_scope; let scope = component.instance_scope;
let map = component.instance_scope_map; let map = component.instance_scope_map;
let pending_assignments = new Set(); let pending_assignments = new Set();
walk(component.instance_script.content, { walk(component.ast.instance.content, {
enter: (node, parent) => { enter: (node, parent) => {
if (map.has(node)) { if (map.has(node)) {
scope = map.get(node); scope = map.get(node);
@ -297,7 +297,7 @@ export default function dom(
}); });
const user_code = component.javascript || ( const user_code = component.javascript || (
component.ast.js.length === 0 && filtered_props.length > 0 !component.ast.instance && !component.ast.module && filtered_props.length > 0
? `let { ${filtered_props.map(x => x.name).join(', ')} } = $$props;` ? `let { ${filtered_props.map(x => x.name).join(', ')} } = $$props;`
: null : null
); );

@ -27,7 +27,7 @@ export default function ssr(
if (component.javascript) { if (component.javascript) {
component.rewrite_props(); component.rewrite_props();
user_code = component.javascript; user_code = component.javascript;
} else if (component.ast.js.length === 0 && component.props.length > 0) { } else if (!component.ast.instance && !component.ast.module && component.props.length > 0) {
const props = component.props.map(prop => prop.as).filter(name => name[0] !== '$'); const props = component.props.map(prop => prop.as).filter(name => name[0] !== '$');
user_code = `let { ${props.join(', ')} } = $$props;` user_code = `let { ${props.join(', ')} } = $$props;`

@ -21,7 +21,8 @@ export interface Parser {
export interface Ast { export interface Ast {
html: Node; html: Node;
css: Node; css: Node;
js: Node; instance: Node;
module: Node;
} }
export interface Warning { export interface Warning {

@ -3,13 +3,13 @@ import dynamicImport from 'acorn-dynamic-import';
const Parser = acorn.Parser.extend(dynamicImport); const Parser = acorn.Parser.extend(dynamicImport);
export const parse = (source: string, options: any) => Parser.parse(source, { export const parse = (source: string) => Parser.parse(source, {
sourceType: 'module', sourceType: 'module',
ecmaVersion: 9, ecmaVersion: 9,
preserveParens: true preserveParens: true
}); });
export const parseExpressionAt = (source: string, index: number, options: any) => Parser.parseExpressionAt(source, index, { export const parseExpressionAt = (source: string, index: number) => Parser.parseExpressionAt(source, index, {
ecmaVersion: 9, ecmaVersion: 9,
preserveParens: true preserveParens: true
}); });

@ -226,9 +226,27 @@ export default function parse(
}, parser.css[1].start); }, parser.css[1].start);
} }
const instance_scripts = parser.js.filter(script => script.context === 'default');
const module_scripts = parser.js.filter(script => script.context === 'module');
if (instance_scripts.length > 1) {
parser.error({
code: `invalid-script`,
message: `A component can only have one instance-level <script> element`
}, instance_scripts[1].start);
}
if (module_scripts.length > 1) {
parser.error({
code: `invalid-script`,
message: `A component can only have one <script context="module"> element`
}, module_scripts[1].start);
}
return { return {
html: parser.html, html: parser.html,
css: parser.css, css: parser.css[0],
js: parser.js, instance: instance_scripts[0],
module: module_scripts[0]
}; };
} }

@ -5,6 +5,29 @@ import { Node } from '../../interfaces';
const scriptClosingTag = '</script>'; const scriptClosingTag = '</script>';
function get_context(parser: Parser, attributes: Node[], start: number) {
const context = attributes.find(attribute => attribute.name === 'context');
if (!context) return 'default';
if (context.value.length !== 1 || context.value[0].type !== 'Text') {
parser.error({
code: 'invalid-script',
message: `context attribute must be static`
}, start);
}
const value = context.value[0].data;
if (value !== 'module') {
parser.error({
code: `invalid-script`,
message: `If the context attribute is supplied, its value must be "module"`
}, context.start);
}
return value;
}
export default function readScript(parser: Parser, start: number, attributes: Node[]) { export default function readScript(parser: Parser, start: number, attributes: Node[]) {
const scriptStart = parser.index; const scriptStart = parser.index;
const scriptEnd = parser.template.indexOf(scriptClosingTag, scriptStart); const scriptEnd = parser.template.indexOf(scriptClosingTag, scriptStart);
@ -30,7 +53,7 @@ export default function readScript(parser: Parser, start: number, attributes: No
return { return {
start, start,
end: parser.index, end: parser.index,
attributes, context: get_context(parser, attributes, start),
content: ast, content: ast,
}; };
} }

@ -31,7 +31,8 @@ describe('parse', () => {
assert.deepEqual(ast.html, expectedOutput.html); assert.deepEqual(ast.html, expectedOutput.html);
assert.deepEqual(ast.css, expectedOutput.css); assert.deepEqual(ast.css, expectedOutput.css);
assert.deepEqual(ast.js, expectedOutput.js); assert.deepEqual(ast.instance, expectedOutput.instance);
assert.deepEqual(ast.module, expectedOutput.module);
} catch (err) { } catch (err) {
if (err.name !== 'ParseError') throw err; if (err.name !== 'ParseError') throw err;
if (!expectedError) throw err; if (!expectedError) throw err;

@ -42,6 +42,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -28,6 +28,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -29,6 +29,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -23,6 +23,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -55,6 +55,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -36,6 +36,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -34,6 +34,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -34,6 +34,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -58,6 +58,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -29,6 +29,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -43,6 +43,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -34,6 +34,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -22,6 +22,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -29,6 +29,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -29,6 +29,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -155,6 +155,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -28,6 +28,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -28,6 +28,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -12,6 +12,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -37,6 +37,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -21,6 +21,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -12,6 +12,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -27,8 +27,7 @@
} }
] ]
}, },
"css": [ "css": {
{
"start": 16, "start": 16,
"end": 56, "end": 56,
"attributes": [], "attributes": [],
@ -91,7 +90,7 @@
"end": 48, "end": 48,
"styles": "\n\tdiv {\n\t\tcolor: red;\n\t}\n" "styles": "\n\tdiv {\n\t\tcolor: red;\n\t}\n"
} }
} },
], "instance": null,
"js": [] "module": null
} }

@ -5,12 +5,11 @@
"type": "Fragment", "type": "Fragment",
"children": [] "children": []
}, },
"css": [], "css": null,
"js": [ "instance": {
{
"start": 0, "start": 0,
"end": 146, "end": 146,
"attributes": [], "context": "default",
"content": { "content": {
"type": "Program", "type": "Program",
"start": 8, "start": 8,
@ -201,5 +200,4 @@
"sourceType": "module" "sourceType": "module"
} }
} }
]
} }

@ -75,6 +75,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -67,6 +67,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -63,6 +63,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -63,6 +63,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -45,6 +45,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -38,6 +38,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -21,6 +21,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -22,6 +22,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -98,6 +98,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -56,6 +56,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -96,6 +96,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -25,6 +25,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -66,6 +66,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -21,6 +21,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -55,6 +55,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -28,6 +28,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -20,12 +20,10 @@
} }
] ]
}, },
"css": [], "instance": {
"js": [
{
"start": 0, "start": 0,
"end": 43, "end": 43,
"attributes": [], "context": "default",
"content": { "content": {
"type": "Program", "type": "Program",
"start": 8, "start": 8,
@ -34,5 +32,4 @@
"sourceType": "module" "sourceType": "module"
} }
} }
]
} }

@ -44,12 +44,10 @@
} }
] ]
}, },
"css": [], "instance": {
"js": [
{
"start": 0, "start": 0,
"end": 77, "end": 77,
"attributes": [], "context": "default",
"content": { "content": {
"type": "Program", "type": "Program",
"start": 8, "start": 8,
@ -85,5 +83,4 @@
"sourceType": "module" "sourceType": "module"
} }
} }
]
} }

@ -44,12 +44,10 @@
} }
] ]
}, },
"css": [], "instance": {
"js": [
{
"start": 0, "start": 0,
"end": 66, "end": 66,
"attributes": [], "context": "default",
"content": { "content": {
"type": "Program", "type": "Program",
"start": 8, "start": 8,
@ -85,5 +83,4 @@
"sourceType": "module" "sourceType": "module"
} }
} }
]
} }

@ -44,12 +44,10 @@
} }
] ]
}, },
"css": [], "instance": {
"js": [
{
"start": 0, "start": 0,
"end": 39, "end": 39,
"attributes": [], "context": "default",
"content": { "content": {
"type": "Program", "type": "Program",
"start": 8, "start": 8,
@ -85,5 +83,4 @@
"sourceType": "module" "sourceType": "module"
} }
} }
]
} }

@ -14,6 +14,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -73,6 +73,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -72,6 +72,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -26,6 +26,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -44,6 +44,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -32,6 +32,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -60,6 +60,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -45,6 +45,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -27,6 +27,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -62,6 +62,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -17,6 +17,7 @@
} }
] ]
}, },
"css": [], "css": null,
"js": [] "instance": null,
"module": null
} }

@ -8,8 +8,8 @@
"character": 30 "character": 30
}, },
"end": { "end": {
"line": 7, "line": 5,
"column": 9, "column": 0,
"character": 58 "character": 30
} }
}] }]

@ -8,8 +8,8 @@
"character": 47 "character": 47
}, },
"end": { "end": {
"line": 7, "line": 5,
"column": 9, "column": 0,
"character": 92 "character": 47
} }
}] }]

@ -9,7 +9,7 @@
}, },
"end": { "end": {
"line": 1, "line": 1,
"column": 22, "column": 8,
"character": 22 "character": 8
} }
}] }]
Loading…
Cancel
Save