You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte/src/generators/shared/utils/getModuleWrapper.ts

253 lines
6.4 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import deindent from '../../../utils/deindent';
import { CompileOptions, ModuleFormat, Node } from '../../../interfaces';
interface Dependency {
name: string;
statements: string[];
source: string;
}
export default function getModuleWrapper(
format: ModuleFormat,
name: string,
options: CompileOptions,
imports: Node[],
source: string
) {
if (format === 'es') return getEsWrapper(name, options, imports, source);
const dependencies = imports.map((declaration, i) => {
const defaultImport = declaration.specifiers.find(
(x: Node) =>
x.type === 'ImportDefaultSpecifier' ||
(x.type === 'ImportSpecifier' && x.imported.name === 'default')
);
const namespaceImport = declaration.specifiers.find(
(x: Node) => x.type === 'ImportNamespaceSpecifier'
);
const namedImports = declaration.specifiers.filter(
(x: Node) =>
x.type === 'ImportSpecifier' && x.imported.name !== 'default'
);
const name = defaultImport || namespaceImport
? (defaultImport || namespaceImport).local.name
: `__import${i}`;
const statements: string[] = [];
namedImports.forEach((specifier: Node) => {
statements.push(
`var ${specifier.local.name} = ${name}.${specifier.imported.name};`
);
});
if (defaultImport) {
statements.push(
`${name} = (${name} && ${name}.__esModule) ? ${name}["default"] : ${name};`
);
}
return { name, statements, source: declaration.source.value };
});
if (format === 'amd') return getAmdWrapper(name, options, dependencies);
if (format === 'cjs') return getCjsWrapper(name, options, dependencies);
if (format === 'iife') return getIifeWrapper(name, options, dependencies);
if (format === 'umd') return getUmdWrapper(name, options, dependencies);
if (format === 'eval') return getEvalWrapper(name, options, dependencies);
throw new Error(`Not implemented: ${format}`);
}
function getEsWrapper(name: string, options: CompileOptions, imports: Node[], source: string) {
const importBlock = imports
.map((declaration: Node) => source.slice(declaration.start, declaration.end))
.join('\n');
return {
intro: importBlock ? importBlock + '\n\n' : '',
outro: `export default ${name};`
};
}
function getAmdWrapper(name: string, options: CompileOptions, dependencies: Dependency[]) {
const sourceString = dependencies.length
? `[${dependencies.map(d => `"${removeExtension(d.source)}"`).join(', ')}], `
: '';
const id = options.amd && options.amd.id;
return {
intro: deindent`
define(${id ? `"${id}", ` : ''}${sourceString}function(${paramString(dependencies)}) { "use strict";
${getCompatibilityStatements(dependencies)}
`,
outro: deindent`
return ${name};
});`
};
}
function getCjsWrapper(name: string, options: CompileOptions, dependencies: Dependency[]) {
const requireBlock = dependencies
.map(d => `var ${d.name} = require("${d.source}");`)
.join('\n\n');
const intro = requireBlock ?
deindent`
"use strict";
${requireBlock}
${getCompatibilityStatements(dependencies)}
` :
deindent`
"use strict";
`;
const outro = `module.exports = ${name};`
return { intro, outro };
}
function getIifeWrapper(name: string, options: CompileOptions, dependencies: Dependency[]) {
if (!options.name) {
throw new Error(`Missing required 'name' option for IIFE export`);
}
const globals = getGlobals(dependencies, options);
return {
intro: deindent`
var ${options.name} = (function(${paramString(dependencies)}) { "use strict";
${getCompatibilityStatements(dependencies)}
`,
outro: `return ${name};\n\n}(${globals.join(', ')}));`
};
}
function getUmdWrapper(name: string, options: CompileOptions, dependencies: Dependency[]) {
if (!options.name) {
throw new Error(`Missing required 'name' option for UMD export`);
}
const amdId = options.amd && options.amd.id ? `'${options.amd.id}', ` : '';
const amdDeps = dependencies.length
? `[${dependencies.map(d => `"${removeExtension(d.source)}"`).join(', ')}], `
: '';
const cjsDeps = dependencies
.map(d => `require("${d.source}")`)
.join(', ');
const globals = getGlobals(dependencies, options);
return {
intro: deindent`
(function(global, factory) {
typeof exports === "object" && typeof module !== "undefined" ? module.exports = factory(${cjsDeps}) :
typeof define === "function" && define.amd ? define(${amdId}${amdDeps}factory) :
(global.${options.name} = factory(${globals}));
}(this, (function (${paramString(dependencies)}) { "use strict";
${getCompatibilityStatements(dependencies)}
`,
outro: deindent`
return ${name};
})));`
};
}
function getEvalWrapper(name: string, options: CompileOptions, dependencies: Dependency[]) {
const globals = getGlobals(dependencies, options);
return {
intro: deindent`
(function (${paramString(dependencies)}) { "use strict";
${getCompatibilityStatements(dependencies)}
`,
outro: `return ${name};\n\n}(${globals.join(', ')}));`
};
}
function paramString(dependencies: Dependency[]) {
return dependencies.map(dep => dep.name).join(', ');
}
function removeExtension(file: string) {
const index = file.lastIndexOf('.');
return ~index ? file.slice(0, index) : file;
}
function getCompatibilityStatements(dependencies: Dependency[]) {
if (!dependencies.length) return null;
const statements: string[] = [];
dependencies.forEach(dependency => {
statements.push(...dependency.statements);
});
return statements.join('\n');
}
function getGlobals(dependencies: Dependency[], options: CompileOptions) {
const { globals, onerror, onwarn } = options;
const globalFn = getGlobalFn(globals);
return dependencies.map(d => {
let name = globalFn(d.source);
if (!name) {
if (d.name.startsWith('__import')) {
const error = new Error(
`Could not determine name for imported module '${d.source}' use options.globals`
);
if (onerror) {
onerror(error);
} else {
throw error;
}
} else {
const warning = {
message: `No name was supplied for imported module '${d.source}'. Guessing '${d.name}', but you should use options.globals`,
};
if (onwarn) {
onwarn(warning);
} else {
console.warn(warning); // eslint-disable-line no-console
}
}
name = d.name;
}
return name;
});
}
function getGlobalFn(globals: any): (id: string) => string {
if (typeof globals === 'function') return globals;
if (typeof globals === 'object') {
return id => globals[id];
}
return () => undefined;
}