IIFE and UMD builds (#27)

pull/31/head
Rich-Harris 8 years ago
parent fa5bbbee9f
commit fb5dd95bb0

@ -503,7 +503,7 @@ export default function generate ( parsed, source, options ) {
compiled.append( finalChunk );
addString( '\n\n' + getOutro( format, constructorName, imports ) );
addString( '\n\n' + getOutro( format, constructorName, options, imports ) );
return {
code: compiled.toString(),

@ -0,0 +1,43 @@
export default function getGlobals ( imports, { globals, onerror, onwarn } ) {
const globalFn = getGlobalFn( globals );
return imports.map( x => {
let name = globalFn( x.source.value );
if ( !name ) {
if ( x.name.startsWith( '__import' ) ) {
const error = new Error( `Could not determine name for imported module '${x.source.value}' use options.globals` );
if ( onerror ) {
onerror( error );
} else {
throw error;
}
}
else {
const warning = {
message: `No name was supplied for imported module '${x.source.value}'. Guessing '${x.name}', but you should use options.globals`
};
if ( onwarn ) {
onwarn( warning );
} else {
console.warn( warning ); // eslint-disable-line no-console
}
}
name = x.name;
}
return name;
});
}
function getGlobalFn ( globals ) {
if ( typeof globals === 'function' ) return globals;
if ( typeof globals === 'object' ) {
return id => globals[ id ];
}
return () => undefined;
}

@ -1,31 +1,29 @@
export default function getIntro ( format, options, imports ) {
const dependencies = imports.map( declaration => {
return {
source: declaration.source.value,
name: declaration.name
};
});
import deindent from './deindent.js';
import getGlobals from './getGlobals.js';
export default function getIntro ( format, options, imports ) {
if ( format === 'es' ) return '';
if ( format === 'amd' ) return getAmdIntro( options.amd, dependencies );
if ( format === 'cjs' ) return getCjsIntro( dependencies );
if ( format === 'amd' ) return getAmdIntro( options, imports );
if ( format === 'cjs' ) return getCjsIntro( options, imports );
if ( format === 'iife' ) return getIifeIntro( options, imports );
if ( format === 'umd' ) return getUmdIntro( options, imports );
throw new Error( `Not implemented: ${format}` );
}
function getAmdIntro ( options = {}, dependencies ) {
const sourceString = dependencies.length ?
`[ ${dependencies.map( dep => `'${dep.source}'` ).join( ', ' )} ], ` :
function getAmdIntro ( options, imports ) {
const sourceString = imports.length ?
`[ ${imports.map( declaration => `'${declaration.source.value}'` ).join( ', ' )} ], ` :
'';
const paramString = dependencies.length ? ` ${dependencies.map( dep => dep.name ).join( ', ' )} ` : '';
const id = options.amd && options.amd.id;
return `define(${options.id ? ` '${options.id}', ` : ''}${sourceString}function (${paramString}) { 'use strict';\n\n`;
return `define(${id ? ` '${id}', ` : ''}${sourceString}function (${paramString( imports )}) { 'use strict';\n\n`;
}
function getCjsIntro ( dependencies ) {
const requireBlock = dependencies
.map( dep => `var ${dep.name} = require( '${dep.source}' );` )
function getCjsIntro ( options, imports ) {
const requireBlock = imports
.map( declaration => `var ${declaration.name} = require( '${declaration.source.value}' );` )
.join( '\n\n' );
if ( requireBlock ) {
@ -34,3 +32,34 @@ function getCjsIntro ( dependencies ) {
return `'use strict';\n\n`;
}
function getIifeIntro ( options, imports ) {
if ( !options.name ) {
throw new Error( `Missing required 'name' option for IIFE export` );
}
return `var ${options.name} = (function (${paramString( imports )}) { 'use strict';\n\n`;
}
function getUmdIntro ( options, imports ) {
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 = imports.length ? `[${imports.map( declaration => `'${declaration.source.value}'` ).join( ', ')}], ` : '';
const cjsDeps = imports.map( declaration => `require('${declaration.source.value}')` ).join( ', ' );
const globalDeps = getGlobals( imports, options );
return 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(${globalDeps}));
}(this, (function (${paramString( imports )}) { 'use strict';` + '\n\n';
}
function paramString ( imports ) {
return imports.length ? ` ${imports.map( dep => dep.name ).join( ', ' )} ` : '';
}

@ -1,4 +1,6 @@
export default function getOutro ( format, name, imports ) {
import getGlobals from './getGlobals.js';
export default function getOutro ( format, name, options, imports ) {
if ( format === 'es' ) {
return `export default ${name};`;
}
@ -11,5 +13,14 @@ export default function getOutro ( format, name, imports ) {
return `module.exports = ${name};`;
}
if ( format === 'iife' ) {
const globals = getGlobals( imports, options );
return `return ${name};\n\n}(${globals.join( ', ' )}));`;
}
if ( format === 'umd' ) {
return `return ${name};\n\n})));`;
}
throw new Error( `Not implemented: ${format}` );
}

@ -7,7 +7,11 @@ export function compile ( source, options = {} ) {
if ( !options.onwarn ) {
options.onwarn = warning => {
console.warn( `(${warning.loc.line}:${warning.loc.column}) ${warning.message}` ); // eslint-disable-line no-console
if ( warning.loc ) {
console.warn( `(${warning.loc.line}:${warning.loc.column}) ${warning.message}` ); // eslint-disable-line no-console
} else {
console.warn( warning.message ); // eslint-disable-line no-console
}
};
}

@ -280,6 +280,67 @@ describe( 'svelte', () => {
});
describe( 'formats', () => {
function testAmd ( code, expectedId, dependencies, html ) {
const fn = new Function( 'define', code );
return env().then( window => {
function define ( id, deps, factory ) {
assert.equal( id, expectedId );
assert.deepEqual( deps, Object.keys( dependencies ) );
const SvelteComponent = factory( ...Object.keys( dependencies ).map( key => dependencies[ key ] ) );
const main = window.document.body.querySelector( 'main' );
const component = new SvelteComponent({ target: main });
assert.htmlEqual( main.innerHTML, html );
component.teardown();
}
define.amd = true;
fn( define );
});
}
function testCjs ( code, dependencyById, html ) {
const fn = new Function( 'module', 'exports', 'require', code );
return env().then( window => {
const module = { exports: {} };
const require = id => {
return dependencyById[ id ];
};
fn( module, module.exports, require );
const SvelteComponent = module.exports;
const main = window.document.body.querySelector( 'main' );
const component = new SvelteComponent({ target: main });
assert.htmlEqual( main.innerHTML, html );
component.teardown();
});
}
function testIife ( code, name, globals, html ) {
const fn = new Function( Object.keys( globals ), `${code}\n\nreturn ${name};` );
return env().then( window => {
const SvelteComponent = fn( ...Object.keys( globals ).map( key => globals[ key ] ) );
const main = window.document.body.querySelector( 'main' );
const component = new SvelteComponent({ target: main });
assert.htmlEqual( main.innerHTML, html );
component.teardown();
});
}
describe( 'amd', () => {
it( 'generates an AMD module', () => {
const source = deindent`
@ -301,23 +362,7 @@ describe( 'svelte', () => {
amd: { id: 'foo' }
});
const fn = new Function( 'define', code );
return env().then( window => {
fn( ( id, dependencies, factory ) => {
assert.equal( id, 'foo' );
assert.deepEqual( dependencies, [ 'answer' ]);
const SvelteComponent = factory( 42 );
const main = window.document.body.querySelector( 'main' );
const component = new SvelteComponent({ target: main });
assert.htmlEqual( main.innerHTML, `<div>42</div>` );
component.teardown();
});
});
return testAmd( code, 'foo', { answer: 42 }, `<div>42</div>` );
});
});
@ -341,25 +386,68 @@ describe( 'svelte', () => {
format: 'cjs'
});
const fn = new Function( 'module', 'require', code );
return testCjs( code, { answer: 42 }, `<div>42</div>` );
});
});
return env().then( window => {
const module = {};
const require = id => {
if ( id === 'answer' ) return 42;
};
describe( 'iife', () => {
it( 'generates a self-executing script', () => {
const source = deindent`
<div>{{answer}}</div>
fn( module, require );
<script>
import answer from 'answer';
const SvelteComponent = module.exports;
export default {
data () {
return { answer };
}
};
</script>
`;
const main = window.document.body.querySelector( 'main' );
const component = new SvelteComponent({ target: main });
const { code } = compile( source, {
format: 'iife',
name: 'Foo',
globals: {
answer: 'answer'
}
});
assert.htmlEqual( main.innerHTML, `<div>42</div>` );
return testIife( code, 'Foo', { answer: 42 }, `<div>42</div>` );
});
});
component.teardown();
describe( 'umd', () => {
it( 'generates a UMD build', () => {
const source = deindent`
<div>{{answer}}</div>
<script>
import answer from 'answer';
export default {
data () {
return { answer };
}
};
</script>
`;
const { code } = compile( source, {
format: 'umd',
name: 'Foo',
globals: {
answer: 'answer'
},
amd: {
id: 'foo'
}
});
return testAmd( code, 'foo', { answer: 42 }, `<div>42</div>` )
.then( () => testCjs( code, { answer: 42 }, `<div>42</div>` ) )
.then( () => testIife( code, 'Foo', { answer: 42 }, `<div>42</div>` ) );
});
});
});

Loading…
Cancel
Save