diff --git a/register.js b/register.js index 25f8aa4309..2c00990911 100644 --- a/register.js +++ b/register.js @@ -1,20 +1,32 @@ -const fs = require('fs'); -const path = require('path'); -const { compile } = require('./compiler.js'); +const fs = require("fs"); +const path = require("path"); +const { compile } = require("./compiler.js"); -const extensions = ['.svelte', '.html']; +const extensions = [".svelte", ".html"]; +let es6Compiler; let compileOptions = {}; +function compileES6(code) { + try { + return es6Compiler(code); + } catch (error) { + console.log(`error compiling esm file ${error}`); + } +} + function capitalise(name) { return name[0].toUpperCase() + name.slice(1); } -function register(options = {}) { +function register(options = {}, es6_compiler) { if (options.extensions) { extensions.forEach(deregisterExtension); options.extensions.forEach(registerExtension); } + // To compile mjs extensions + if (es6_compiler) es6Compiler = es6_compiler; + compileOptions = Object.assign({}, options); delete compileOptions.extensions; } @@ -24,33 +36,51 @@ function deregisterExtension(extension) { } function registerExtension(extension) { - require.extensions[extension] = function(module, filename) { - const name = path.parse(filename).name - .replace(/^\d/, '_$&') - .replace(/[^a-zA-Z0-9_$]/g, ''); + require.extensions[extension] = function (module, filename) { + const name = path + .parse(filename) + .name.replace(/^\d/, "_$&") + .replace(/[^a-zA-Z0-9_$]/g, ""); const options = Object.assign({}, compileOptions, { filename, name: capitalise(name), - generate: 'ssr', - format: 'cjs' + generate: compileOptions.generate || "ssr", + format: "cjs", }); - const { js, warnings } = compile(fs.readFileSync(filename, 'utf-8'), options); - + const { js, css, ast, warnings, vars, stats } = compile( + fs.readFileSync(filename, "utf-8"), + options + ); + if (options.dev) { - warnings.forEach(warning => { + warnings.forEach((warning) => { console.warn(`\nSvelte Warning in ${warning.filename}:`); console.warn(warning.message); console.warn(warning.frame); - }) + }); } - return module._compile(js.code, filename); + let compiled = module._compile(js.code, filename); + return compiled; }; } -registerExtension('.svelte'); -registerExtension('.html'); +registerExtension(".svelte"); +registerExtension(".html"); + +// To handle mixture of CJS and ESM modules . +require.extensions[".mjs"] = function (module, filename) { + try { + if (es6Compiler == null) { + throw `error es6Compiler is not set`; + } + let code = compileES6(fs.readFileSync(filename, "utf-8")).code; + return module._compile(code, filename); + } catch (error) { + console.log(`error ${error}`); + } +}; module.exports = register; diff --git a/src/compiler/compile/render_dom/Renderer.ts b/src/compiler/compile/render_dom/Renderer.ts index c02d646ebf..75a9a096f0 100644 --- a/src/compiler/compile/render_dom/Renderer.ts +++ b/src/compiler/compile/render_dom/Renderer.ts @@ -1,11 +1,18 @@ -import Block from './Block'; -import { CompileOptions, Var } from '../../interfaces'; -import Component from '../Component'; -import FragmentWrapper from './wrappers/Fragment'; -import { x } from 'code-red'; -import { Node, Identifier, MemberExpression, Literal, Expression, BinaryExpression } from 'estree'; -import flatten_reference from '../utils/flatten_reference'; -import { reserved_keywords } from '../utils/reserved_keywords'; +import Block from "./Block"; +import { CompileOptions, Var } from "../../interfaces"; +import Component from "../Component"; +import FragmentWrapper from "./wrappers/Fragment"; +import { x } from "code-red"; +import { + Node, + Identifier, + MemberExpression, + Literal, + Expression, + BinaryExpression, +} from "estree"; +import flatten_reference from "../utils/flatten_reference"; +import { reserved_keywords } from "../utils/reserved_keywords"; interface ContextMember { name: string; @@ -32,7 +39,15 @@ export default class Renderer { blocks: Array = []; readonly: Set = new Set(); meta_bindings: Array = []; // initial values for e.g. window.innerWidth, if there's a meta tag - binding_groups: Map Node; is_context: boolean; contexts: string[]; index: number }> = new Map(); + binding_groups: Map< + string, + { + binding_group: (to_reference?: boolean) => Node; + is_context: boolean; + contexts: string[]; + index: number; + } + > = new Map(); block: Block; fragment: FragmentWrapper; @@ -45,33 +60,37 @@ export default class Renderer { this.options = options; this.locate = component.locate; // TODO messy - this.file_var = options.dev && this.component.get_unique_name('file'); + this.file_var = options.dev && this.component.get_unique_name("file"); - component.vars.filter(v => !v.hoistable || (v.export_name && !v.module)).forEach(v => this.add_to_context(v.name)); + component.vars + .filter((v) => !v.hoistable || (v.export_name && !v.module)) + .forEach((v) => this.add_to_context(v.name)); // ensure store values are included in context - component.vars.filter(v => v.subscribable).forEach(v => this.add_to_context(`$${v.name}`)); + component.vars + .filter((v) => v.subscribable) + .forEach((v) => this.add_to_context(`$${v.name}`)); - reserved_keywords.forEach(keyword => { + reserved_keywords.forEach((keyword) => { if (component.var_lookup.has(keyword)) { this.add_to_context(keyword); } }); if (component.slots.size > 0) { - this.add_to_context('$$scope'); - this.add_to_context('$$slots'); + this.add_to_context("$$scope"); + this.add_to_context("$$slots"); } if (this.binding_groups.size > 0) { - this.add_to_context('$$binding_groups'); + this.add_to_context("$$binding_groups"); } // main block this.block = new Block({ renderer: this, name: null, - type: 'component', + type: "component", key: null, bindings: new Map(), @@ -91,7 +110,7 @@ export default class Renderer { ); // TODO messy - this.blocks.forEach(block => { + this.blocks.forEach((block) => { if (block instanceof Block) { block.assign_variable_names(); } @@ -103,7 +122,7 @@ export default class Renderer { this.context_overflow = this.context.length > 31; - this.context.forEach(member => { + this.context.forEach((member) => { const { variable } = member; if (variable) { member.priority += 2; @@ -124,8 +143,12 @@ export default class Renderer { } }); - this.context.sort((a, b) => (b.priority - a.priority) || ((a.index.value as number) - (b.index.value as number))); - this.context.forEach((member, i) => member.index.value = i); + this.context.sort( + (a, b) => + b.priority - a.priority || + (a.index.value as number) - (b.index.value as number) + ); + this.context.forEach((member, i) => (member.index.value = i)); let i = this.context.length; while (i--) { @@ -143,11 +166,11 @@ export default class Renderer { if (!this.context_lookup.has(name)) { const member: ContextMember = { name, - index: { type: 'Literal', value: this.context.length }, // index is updated later, but set here to preserve order within groups + index: { type: "Literal", value: this.context.length }, // index is updated later, but set here to preserve order within groups is_contextual: false, is_non_contextual: false, // shadowed vars could be contextual and non-contextual variable: null, - priority: 0 + priority: 0, }; this.context_lookup.set(name, member); @@ -171,23 +194,27 @@ export default class Renderer { const variable = this.component.var_lookup.get(name); const member = this.context_lookup.get(name); - if (variable && (variable.subscribable && (variable.reassigned || variable.export_name))) { - return x`${`$$subscribe_${name}`}($$invalidate(${member.index}, ${value || name}))`; + if ( + variable && + variable.subscribable && + (variable.reassigned || variable.export_name) + ) { + return x`${`$$subscribe_${name}`}($$invalidate(${member.index}, ${ + value || name + }))`; } - if (name[0] === '$' && name[1] !== '$') { + if (name[0] === "$" && name[1] !== "$") { return x`${name.slice(1)}.set(${value || name})`; } if ( - variable && ( - variable.module || ( - !variable.referenced && + variable && + (variable.module || + (!variable.referenced && !variable.is_reactive_dependency && !variable.export_name && - !name.startsWith('$$') - ) - ) + !name.startsWith("$$"))) ) { return value || name; } @@ -199,32 +226,32 @@ export default class Renderer { // if this is a reactive declaration, invalidate dependencies recursively const deps = new Set([name]); - deps.forEach(name => { - const reactive_declarations = this.component.reactive_declarations.filter(x => - x.assignees.has(name) + deps.forEach((name) => { + const reactive_declarations = this.component.reactive_declarations.filter( + (x) => x.assignees.has(name) ); - reactive_declarations.forEach(declaration => { - declaration.dependencies.forEach(name => { + reactive_declarations.forEach((declaration) => { + declaration.dependencies.forEach((name) => { deps.add(name); }); }); }); // TODO ideally globals etc wouldn't be here in the first place - const filtered = Array.from(deps).filter(n => this.context_lookup.has(n)); + const filtered = Array.from(deps).filter((n) => this.context_lookup.has(n)); if (!filtered.length) return null; return filtered - .map(n => x`$$invalidate(${this.context_lookup.get(n).index}, ${n})`) + .map((n) => x`$$invalidate(${this.context_lookup.get(n).index}, ${n})`) .reduce((lhs, rhs) => x`${lhs}, ${rhs}`); } dirty(names, is_reactive_declaration = false): Expression { const renderer = this; - const dirty = (is_reactive_declaration - ? x`$$self.$$.dirty` - : x`#dirty`) as Identifier | MemberExpression; + const dirty = (is_reactive_declaration ? x`$$self.$$.dirty` : x`#dirty`) as + | Identifier + | MemberExpression; const get_bitmask = () => { const bitmask: BitMasks = []; @@ -239,7 +266,7 @@ export default class Renderer { const value = member.index.value as number; const i = (value / 31) | 0; - const n = 1 << (value % 31); + const n = 1 << value % 31; if (!bitmask[i]) bitmask[i] = { n: 0, names: [] }; @@ -255,30 +282,34 @@ export default class Renderer { // the expression lazily. TODO would be better if // context was determined before rendering, so that // this indirection was unnecessary - type: 'ParenthesizedExpression', + type: "ParenthesizedExpression", get expression() { const bitmask = get_bitmask(); if (!bitmask.length) { - return x`${dirty} & /*${names.join(', ')}*/ 0` as BinaryExpression; + return x`${dirty} & /*${names.join(", ")}*/ 0` as BinaryExpression; } if (renderer.context_overflow) { return bitmask .map((b, i) => ({ b, i })) .filter(({ b }) => b) - .map(({ b, i }) => x`${dirty}[${i}] & /*${b.names.join(', ')}*/ ${b.n}`) + .map( + ({ b, i }) => x`${dirty}[${i}] & /*${b.names.join(", ")}*/ ${b.n}` + ) .reduce((lhs, rhs) => x`${lhs} | ${rhs}`); } - return x`${dirty} & /*${names.join(', ')}*/ ${bitmask[0].n}` as BinaryExpression; - } + return x`${dirty} & /*${names.join(", ")}*/ ${ + bitmask[0].n + }` as BinaryExpression; + }, } as any; } reference(node: string | Identifier | MemberExpression) { - if (typeof node === 'string') { - node = { type: 'Identifier', name: node }; + if (typeof node === "string") { + node = { type: "Identifier", name: node }; } const { name, nodes } = flatten_reference(node); diff --git a/src/runtime/internal/ssr.ts b/src/runtime/internal/ssr.ts index 646a81d817..a923882e15 100644 --- a/src/runtime/internal/ssr.ts +++ b/src/runtime/internal/ssr.ts @@ -1,6 +1,6 @@ -import { set_current_component, current_component } from './lifecycle'; -import { run_all, blank_object } from './utils'; -import { boolean_attributes } from '../../compiler/compile/render_ssr/handlers/shared/boolean_attributes'; +import { set_current_component, current_component } from "./lifecycle"; +import { run_all, blank_object } from "./utils"; +import { boolean_attributes } from "../../compiler/compile/render_ssr/handlers/shared/boolean_attributes"; export const invalid_attribute_name_character = /[\s'">/=\u{FDD0}-\u{FDEF}\u{FFFE}\u{FFFF}\u{1FFFE}\u{1FFFF}\u{2FFFE}\u{2FFFF}\u{3FFFE}\u{3FFFF}\u{4FFFE}\u{4FFFF}\u{5FFFE}\u{5FFFF}\u{6FFFE}\u{6FFFF}\u{7FFFE}\u{7FFFF}\u{8FFFE}\u{8FFFF}\u{9FFFE}\u{9FFFF}\u{AFFFE}\u{AFFFF}\u{BFFFE}\u{BFFFF}\u{CFFFE}\u{CFFFF}\u{DFFFE}\u{DFFFF}\u{EFFFE}\u{EFFFF}\u{FFFFE}\u{FFFFF}\u{10FFFE}\u{10FFFF}]/u; // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 @@ -12,12 +12,12 @@ export function spread(args, classes_to_add) { if (attributes.class == null) { attributes.class = classes_to_add; } else { - attributes.class += ' ' + classes_to_add; + attributes.class += " " + classes_to_add; } } - let str = ''; + let str = ""; - Object.keys(attributes).forEach(name => { + Object.keys(attributes).forEach((name) => { if (invalid_attribute_name_character.test(name)) return; const value = attributes[name]; @@ -25,7 +25,9 @@ export function spread(args, classes_to_add) { else if (boolean_attributes.has(name.toLowerCase())) { if (value) str += " " + name; } else if (value != null) { - str += ` ${name}="${String(value).replace(/"/g, '"').replace(/'/g, ''')}"`; + str += ` ${name}="${String(value) + .replace(/"/g, """) + .replace(/'/g, "'")}"`; } }); @@ -33,19 +35,19 @@ export function spread(args, classes_to_add) { } export const escaped = { - '"': '"', - "'": ''', - '&': '&', - '<': '<', - '>': '>' + '"': """, + "'": "'", + "&": "&", + "<": "<", + ">": ">", }; export function escape(html) { - return String(html).replace(/["'&<>]/g, match => escaped[match]); + return String(html).replace(/["'&<>]/g, (match) => escaped[match]); } export function each(items, fn) { - let str = ''; + let str = ""; for (let i = 0; i < items.length; i += 1) { str += fn(items[i], i); } @@ -53,22 +55,25 @@ export function each(items, fn) { } export const missing_component = { - $$render: () => '' + $$render: () => "", }; export function validate_component(component, name) { + if (component.default) component = component.default; if (!component || !component.$$render) { - if (name === 'svelte:component') name += ' this={...}'; - throw new Error(`<${name}> is not a valid SSR component. You may need to review your build config to ensure that dependencies are compiled, rather than imported as pre-compiled modules`); + if (name === "svelte:component") name += " this={...}"; + throw new Error( + `<${name}> is not a valid SSR component. You may need to review your build config to ensure that dependencies are compiled, rather than imported as pre-compiled modules` + ); } return component; } export function debug(file, line, column, values) { - console.log(`{@debug} ${file ? file + ' ' : ''}(${line}:${column})`); // eslint-disable-line no-console + console.log(`{@debug} ${file ? file + " " : ""}(${line}:${column})`); // eslint-disable-line no-console console.log(values); // eslint-disable-line no-console - return ''; + return ""; } let on_destroy; @@ -85,7 +90,7 @@ export function create_ssr_component(fn) { on_mount: [], before_update: [], after_update: [], - callbacks: blank_object() + callbacks: blank_object(), }; set_current_component({ $$ }); @@ -107,7 +112,7 @@ export function create_ssr_component(fn) { map: null; code: string; }>; - } = { title: '', head: '', css: new Set() }; + } = { title: "", head: "", css: new Set() }; const html = $$render(result, props, {}, options); @@ -116,22 +121,32 @@ export function create_ssr_component(fn) { return { html, css: { - code: Array.from(result.css).map(css => css.code).join('\n'), - map: null // TODO + code: Array.from(result.css) + .map((css) => css.code) + .join("\n"), + map: null, // TODO }, - head: result.title + result.head + head: result.title + result.head, }; }, - $$render + $$render, }; } export function add_attribute(name, value, boolean) { - if (value == null || (boolean && !value)) return ''; - return ` ${name}${value === true ? '' : `=${typeof value === 'string' ? JSON.stringify(escape(value)) : `"${value}"`}`}`; + if (value == null || (boolean && !value)) return ""; + return ` ${name}${ + value === true + ? "" + : `=${ + typeof value === "string" + ? JSON.stringify(escape(value)) + : `"${value}"` + }` + }`; } export function add_classes(classes) { return classes ? ` class="${classes}"` : ``; -} \ No newline at end of file +}