[feat] enable export ... from (#6574)

pull/6593/head
Tan Li Hau 3 years ago committed by GitHub
parent 4d677d5643
commit c550f604f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -24,7 +24,7 @@ import TemplateScope from './nodes/shared/TemplateScope';
import fuzzymatch from '../utils/fuzzymatch';
import get_object from './utils/get_object';
import Slot from './nodes/Slot';
import { Node, ImportDeclaration, Identifier, Program, ExpressionStatement, AssignmentExpression, Literal } from 'estree';
import { Node, ImportDeclaration, ExportNamedDeclaration, Identifier, Program, ExpressionStatement, AssignmentExpression, Literal, ExportDefaultDeclaration, ExportAllDeclaration } from 'estree';
import add_to_set from './utils/add_to_set';
import check_graph_for_cycles from './utils/check_graph_for_cycles';
import { print, x, b } from 'code-red';
@ -70,6 +70,8 @@ export default class Component {
var_lookup: Map<string, Var> = new Map();
imports: ImportDeclaration[] = [];
exports_from: ExportNamedDeclaration[] = [];
instance_exports_from: ExportNamedDeclaration[] = [];
hoistable_nodes: Set<Node> = new Set();
node_for_declaration: Map<string, Node> = new Map();
@ -333,7 +335,8 @@ export default class Component {
.map(variable => ({
name: variable.name,
as: variable.export_name
}))
})),
this.exports_from
);
css = compile_options.customElement
@ -492,22 +495,27 @@ export default class Component {
this.imports.push(node);
}
extract_exports(node) {
extract_exports(node, module_script = false) {
const ignores = extract_svelte_ignore_from_comments(node);
if (ignores.length) this.push_ignores(ignores);
const result = this._extract_exports(node);
const result = this._extract_exports(node, module_script);
if (ignores.length) this.pop_ignores();
return result;
}
private _extract_exports(node) {
private _extract_exports(node: ExportDefaultDeclaration | ExportNamedDeclaration | ExportAllDeclaration, module_script) {
if (node.type === 'ExportDefaultDeclaration') {
return this.error(node, compiler_errors.default_export);
return this.error(node as any, compiler_errors.default_export);
}
if (node.type === 'ExportNamedDeclaration') {
if (node.source) {
return this.error(node, compiler_errors.not_implemented);
if (module_script) {
this.exports_from.push(node);
} else {
this.instance_exports_from.push(node);
}
return null;
}
if (node.declaration) {
if (node.declaration.type === 'VariableDeclaration') {
@ -516,7 +524,7 @@ export default class Component {
const variable = this.var_lookup.get(name);
variable.export_name = name;
if (variable.writable && !(variable.referenced || variable.referenced_from_script || variable.subscribable)) {
this.warn(declarator, compiler_warnings.unused_export_let(this.name.name, name));
this.warn(declarator as any, compiler_warnings.unused_export_let(this.name.name, name));
}
});
});
@ -536,7 +544,7 @@ export default class Component {
variable.export_name = specifier.exported.name;
if (variable.writable && !(variable.referenced || variable.referenced_from_script || variable.subscribable)) {
this.warn(specifier, compiler_warnings.unused_export_let(this.name.name, specifier.exported.name));
this.warn(specifier as any, compiler_warnings.unused_export_let(this.name.name, specifier.exported.name));
}
}
});
@ -612,7 +620,7 @@ export default class Component {
}
if (/^Export/.test(node.type)) {
const replacement = this.extract_exports(node);
const replacement = this.extract_exports(node, true);
if (replacement) {
body[i] = replacement;
} else {

@ -174,10 +174,6 @@ export default {
code: 'default-export',
message: 'A component cannot have a default export'
},
not_implemented: {
code: 'not-implemented',
message: 'A component currently cannot have an export ... from'
},
illegal_declaration: {
code: 'illegal-declaration',
message: 'The $ prefix is reserved, and cannot be used for variable and import names'

@ -1,7 +1,7 @@
import list from '../utils/list';
import { ModuleFormat } from '../interfaces';
import { b, x } from 'code-red';
import { Identifier, ImportDeclaration } from 'estree';
import { Identifier, ImportDeclaration, ExportNamedDeclaration } from 'estree';
const wrappers = { esm, cjs };
@ -19,20 +19,21 @@ export default function create_module(
helpers: Array<{ name: string; alias: Identifier }>,
globals: Array<{ name: string; alias: Identifier }>,
imports: ImportDeclaration[],
module_exports: Export[]
module_exports: Export[],
exports_from: ExportNamedDeclaration[]
) {
const internal_path = `${sveltePath}/internal`;
helpers.sort((a, b) => (a.name < b.name) ? -1 : 1);
globals.sort((a, b) => (a.name < b.name) ? -1 : 1);
const formatter = wrappers[format];
if (format === 'esm') {
return esm(program, name, banner, sveltePath, internal_path, helpers, globals, imports, module_exports);
if (!formatter) {
throw new Error(`options.format is invalid (must be ${list(Object.keys(wrappers))})`);
}
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))})`);
return formatter(program, name, banner, sveltePath, internal_path, helpers, globals, imports, module_exports, exports_from);
}
function edit_source(source, sveltePath) {
@ -76,7 +77,8 @@ function esm(
helpers: Array<{ name: string; alias: Identifier }>,
globals: Array<{ name: string; alias: Identifier }>,
imports: ImportDeclaration[],
module_exports: Export[]
module_exports: Export[],
exports_from: ExportNamedDeclaration[]
) {
const import_declaration = {
type: 'ImportDeclaration',
@ -94,6 +96,9 @@ function esm(
imports.forEach(node => {
node.source.value = edit_source(node.source.value, sveltePath);
});
exports_from.forEach(node => {
node.source!.value = edit_source(node.source!.value, sveltePath);
});
const exports = module_exports.length > 0 && {
type: 'ExportNamedDeclaration',
@ -110,6 +115,7 @@ function esm(
${import_declaration}
${internal_globals}
${imports}
${exports_from}
${program.body}
@ -127,7 +133,8 @@ function cjs(
helpers: Array<{ name: string; alias: Identifier }>,
globals: Array<{ name: string; alias: Identifier }>,
imports: ImportDeclaration[],
module_exports: Export[]
module_exports: Export[],
exports_from: ExportNamedDeclaration[]
) {
const internal_requires = {
type: 'VariableDeclaration',
@ -183,6 +190,13 @@ function cjs(
const exports = module_exports.map(x => b`exports.${{ type: 'Identifier', name: x.as }} = ${{ type: 'Identifier', name: x.name }};`);
const user_exports_from = exports_from.map(node => {
const init = x`require("${edit_source(node.source.value, sveltePath)}")`;
return node.specifiers.map(specifier => {
return b`exports.${specifier.exported} = ${init}.${specifier.local};`;
});
});
program.body = b`
/* ${banner} */
@ -190,6 +204,7 @@ function cjs(
${internal_requires}
${internal_globals}
${user_requires}
${user_exports_from}
${program.body}

@ -6,7 +6,7 @@ import { walk } from 'estree-walker';
import { extract_names, Scope } from 'periscopic';
import { invalidate } from './invalidate';
import Block from './Block';
import { ClassDeclaration, FunctionExpression, Node, Statement, ObjectExpression, Expression } from 'estree';
import { ImportDeclaration, ClassDeclaration, FunctionExpression, Node, Statement, ObjectExpression, Expression } from 'estree';
import { apply_preprocessor_sourcemap } from '../../utils/mapped_code';
import { RawSourceMap, DecodedSourceMap } from '@ampproject/remapping/dist/types/types';
import { flatten } from '../../utils/flatten';
@ -174,6 +174,46 @@ export default function dom(
}
});
component.instance_exports_from.forEach(exports_from => {
const import_declaration = {
...exports_from,
type: 'ImportDeclaration',
specifiers: [],
source: exports_from.source
};
component.imports.push(import_declaration as ImportDeclaration);
exports_from.specifiers.forEach(specifier => {
if (component.component_options.accessors) {
const name = component.get_unique_name(specifier.exported.name);
import_declaration.specifiers.push({
...specifier,
type: 'ImportSpecifier',
imported: specifier.local,
local: name
});
accessors.push({
type: 'MethodDefinition',
kind: 'get',
key: { type: 'Identifier', name: specifier.exported.name },
value: x`function() {
return ${name}
}`
});
} else if (component.compile_options.dev) {
accessors.push({
type: 'MethodDefinition',
kind: 'get',
key: { type: 'Identifier', name: specifier.exported.name },
value: x`function() {
throw new @_Error("<${component.tag}>: Props cannot be read directly from the component instance unless compiling with 'accessors: true' or '<svelte:options accessors/>'");
}`
});
}
});
});
if (component.compile_options.dev) {
// checking that expected ones were passed
const expected = props.filter(prop => prop.writable && !prop.initialised);

@ -0,0 +1,5 @@
export default {
options: {
accessors: true
}
};

@ -0,0 +1,34 @@
/* generated by Svelte vX.Y.Z */
import { SvelteComponent, init, safe_not_equal } from "svelte/internal";
import { f as f_1, g as g_1 } from './d';
import { h as h_1 } from './e';
import { i as j } from './f';
export { d as e } from './c';
export { c } from './b';
export { a, b } from './a';
class Component extends SvelteComponent {
constructor(options) {
super();
init(this, options, null, null, safe_not_equal, {});
}
get f() {
return f_1;
}
get g() {
return g_1;
}
get h() {
return h_1;
}
get j() {
return j;
}
}
export default Component;

@ -0,0 +1,11 @@
<script context="module">
export { a, b } from './a';
export { c } from './b';
export { d as e } from './c';
</script>
<script>
export { f, g } from './d';
export { h } from './e';
export { i as j } from './f';
</script>

@ -0,0 +1,6 @@
export default {
options: {
accessors: true,
format: 'cjs'
}
};

@ -0,0 +1,36 @@
/* generated by Svelte vX.Y.Z */
"use strict";
const { SvelteComponent, init, safe_not_equal } = require("svelte/internal");
const { f: f_1, g: g_1 } = require("./d");
const { h: h_1 } = require("./e");
const { i: j } = require("./f");
exports.e = require("./c").d;
exports.c = require("./b").c;
exports.a = require("./a").a;
exports.b = require("./a").b;
class Component extends SvelteComponent {
constructor(options) {
super();
init(this, options, null, null, safe_not_equal, {});
}
get f() {
return f_1;
}
get g() {
return g_1;
}
get h() {
return h_1;
}
get j() {
return j;
}
}
exports.default = Component;

@ -0,0 +1,11 @@
<script context="module">
export { a, b } from './a';
export { c } from './b';
export { d as e } from './c';
</script>
<script>
export { f, g } from './d';
export { h } from './e';
export { i as j } from './f';
</script>

@ -0,0 +1,18 @@
/* generated by Svelte vX.Y.Z */
import { SvelteComponent, init, safe_not_equal } from "svelte/internal";
import './d';
import './e';
import './f';
export { d as e } from './c';
export { c } from './b';
export { a, b } from './a';
class Component extends SvelteComponent {
constructor(options) {
super();
init(this, options, null, null, safe_not_equal, {});
}
}
export default Component;

@ -0,0 +1,11 @@
<script context="module">
export { a, b } from './a';
export { c } from './b';
export { d as e } from './c';
</script>
<script>
export { f, g } from './d';
export { h } from './e';
export { i as j } from './f';
</script>

@ -0,0 +1,23 @@
<script context="module">
export { a, b } from './B.svelte';
export { c as d } from './B.svelte';
</script>
<script>
export { d, e } from './B.svelte';
export { f as g } from './B.svelte';
let e = 123;
let b = 234;
function foo() {
e = 456;
b = 567;
}
</script>
a: {typeof a}<br />
b: {typeof b}<br />
c: {typeof c}<br />
d: {typeof d}<br />
e: {typeof e}<br />
f: {typeof f}<br />
g: {typeof g}<br />

@ -0,0 +1,8 @@
<script context="module">
export const a = 'a';
export const b = 'b';
export const c = 'c';
export const d = 'd';
export const e = 'e';
export const f = 'f';
</script>

@ -0,0 +1,28 @@
export default {
html: `
a,b,undefined,c
<br />
a: undefined<br />
b: number<br />
c: undefined<br />
d: undefined<br />
e: number<br />
f: undefined<br />
g: undefined<br />
<br />
{"d":"d","e":"e","g":"f"}
`,
ssrHtml: `
a,b,undefined,c
<br />
a: undefined<br />
b: number<br />
c: undefined<br />
d: undefined<br />
e: number<br />
f: undefined<br />
g: undefined<br />
<br />
{}
`
};

@ -0,0 +1,21 @@
<script>
import A, { a, b, c, d } from './A.svelte';
import {onMount} from 'svelte';
let component;
let props = {};
onMount(() => {
props = {
d: component.d,
e: component.e,
f: component.f,
g: component.g,
};
});
</script>
{a},{b},{c},{d}
<br />
<A bind:this={component} />
<br />
{JSON.stringify(props)}
Loading…
Cancel
Save