diff --git a/.eslintignore b/.eslintignore index bb90b88b7b87..1682c3706de6 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1 +1,2 @@ src/shared +shared.js diff --git a/.gitignore b/.gitignore index fc40bd1c5568..329472f10601 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ node_modules compiler ssr +shared.js +scratch !test/compiler !test/ssr .nyc_output @@ -9,4 +11,3 @@ coverage coverage.lcov test/sourcemaps/*/output.js test/sourcemaps/*/output.js.map -scratch diff --git a/package.json b/package.json index 8ff0f7adf875..d50e07249a4e 100644 --- a/package.json +++ b/package.json @@ -15,9 +15,10 @@ "codecov": "codecov", "precodecov": "npm run coverage", "lint": "eslint src test/*.js", - "build": "npm run build:main && npm run build:ssr", - "build:main": "rollup -c rollup.config.main.js", - "build:ssr": "rollup -c rollup.config.ssr.js", + "build": "npm run build:main && npm run build:shared && npm run build:ssr", + "build:main": "rollup -c rollup/rollup.config.main.js", + "build:shared": "rollup -c rollup/rollup.config.shared.js", + "build:ssr": "rollup -c rollup/rollup.config.ssr.js", "pretest": "npm run build", "prepublish": "npm run lint && npm run build" }, diff --git a/rollup.config.main.js b/rollup/rollup.config.main.js similarity index 100% rename from rollup.config.main.js rename to rollup/rollup.config.main.js diff --git a/rollup/rollup.config.shared.js b/rollup/rollup.config.shared.js new file mode 100644 index 000000000000..1dda31ee6b33 --- /dev/null +++ b/rollup/rollup.config.shared.js @@ -0,0 +1,5 @@ +export default { + entry: 'src/shared/index.js', + dest: 'shared.js', + format: 'es' +}; diff --git a/rollup.config.ssr.js b/rollup/rollup.config.ssr.js similarity index 100% rename from rollup.config.ssr.js rename to rollup/rollup.config.ssr.js diff --git a/src/generators/dom/index.js b/src/generators/dom/index.js index 2cc549f36230..e64c0c424995 100644 --- a/src/generators/dom/index.js +++ b/src/generators/dom/index.js @@ -302,15 +302,26 @@ export default function dom ( parsed, source, options, names ) { builders.main.addBlock( `${name}.prototype = template.methods;` ); } - builders.main.addBlock( deindent` - ${name}.prototype.get = ${shared.get}; + const standalone = options.standalone !== false; + + builders.main.addBlock( standalone ? + deindent` + ${name}.prototype.get = ${shared.get}; - ${name}.prototype.fire = ${shared.fire}; + ${name}.prototype.fire = ${shared.fire}; - ${name}.prototype.observe = ${shared.observe}; + ${name}.prototype.observe = ${shared.observe}; - ${name}.prototype.on = ${shared.on}; + ${name}.prototype.on = ${shared.on}; + ` : + deindent` + ${name}.prototype.get = get; + ${name}.prototype.fire = fire; + ${name}.prototype.observe = observe; + ${name}.prototype.on = on; + ` ); + builders.main.addBlock( deindent` ${name}.prototype.set = function set ( newState ) { ${builders.set} }; @@ -325,12 +336,23 @@ export default function dom ( parsed, source, options, names ) { }; ` ); - builders.main.addBlock( shared.dispatchObservers.toString() ); + if ( standalone ) { + builders.main.addBlock( shared.dispatchObservers.toString() ); - Object.keys( generator.uses ).forEach( key => { - const fn = shared[ key ]; // eslint-disable-line import/namespace - builders.main.addBlock( fn.toString() ); - }); + Object.keys( generator.uses ).forEach( key => { + const fn = shared[ key ]; // eslint-disable-line import/namespace + builders.main.addBlock( fn.toString() ); + }); + } else { + if ( format !== 'es' ) { + throw new Error( `Non-standalone components must be compiled to ES2015 modules (format: 'es')` ); + } + + const names = [ 'get', 'fire', 'observe', 'on', 'dispatchObservers' ].concat( Object.keys( generator.uses ) ); + builders.main.addLineAtStart( + `import { ${names.join( ', ' )} } from 'svelte/shared.js'` + ); + } return generator.generate( builders.main.toString(), options, { name, format } ); } diff --git a/test/generate.js b/test/generate.js index 2c996fd1ca94..387944dfc4d2 100644 --- a/test/generate.js +++ b/test/generate.js @@ -6,17 +6,16 @@ import * as acorn from 'acorn'; import { svelte, env, setupHtmlEqual } from './helpers.js'; -const cache = {}; - let showCompiledCode = false; let compileOptions = null; require.extensions[ '.html' ] = function ( module, filename ) { const options = Object.assign({ filename }, compileOptions ); - const code = cache[ filename ] || ( cache[ filename ] = svelte.compile( fs.readFileSync( filename, 'utf-8' ), options ).code ); + const { code } = svelte.compile( fs.readFileSync( filename, 'utf-8' ), options ); + if ( showCompiledCode ) console.log( addLineNumbers( code ) ); // eslint-disable-line no-console - return module._compile( code, filename ); + return module._compile( code.replace( 'svelte/shared.js', path.resolve( 'shared.js' ) ), filename ); }; function addLineNumbers ( code ) { @@ -30,7 +29,9 @@ function addLineNumbers ( code ) { function loadConfig ( dir ) { try { - return require( `./generator/${dir}/_config.js` ).default; + const resolved = require.resolve( `./generator/${dir}/_config.js` ); + delete require.cache[ resolved ]; + return require( resolved ).default; } catch ( err ) { if ( err.code === 'E_NOT_FOUND' ) { return {}; @@ -43,7 +44,7 @@ function loadConfig ( dir ) { describe( 'generate', () => { before( setupHtmlEqual ); - fs.readdirSync( 'test/generator' ).forEach( dir => { + function runTest ( dir, standalone ) { if ( dir[0] === '.' ) return; const config = loadConfig( dir ); @@ -53,6 +54,7 @@ describe( 'generate', () => { showCompiledCode = config.show; compileOptions = config.compileOptions || {}; + compileOptions.standalone = standalone; try { const source = fs.readFileSync( `test/generator/${dir}/main.html`, 'utf-8' ); @@ -78,7 +80,9 @@ describe( 'generate', () => { throw err; } - cache[ path.resolve( `test/generator/${dir}/main.html` ) ] = code; + Object.keys( require.cache ).filter( x => x.endsWith( '.html' ) ).forEach( file => { + delete require.cache[ file ]; + }); let SvelteComponent; @@ -117,5 +121,17 @@ describe( 'generate', () => { throw err; }); }); + } + + describe( 'standalone', () => { + fs.readdirSync( 'test/generator' ).forEach( dir => { + runTest( dir, true ); + }); + }); + + describe( 'non-standalone', () => { + fs.readdirSync( 'test/generator' ).forEach( dir => { + runTest( dir, false ); + }); }); }); diff --git a/test/generator/component-ref/Widget.html b/test/generator/component-ref/Widget.html index 460a2a1c0195..c5a462601a6f 100644 --- a/test/generator/component-ref/Widget.html +++ b/test/generator/component-ref/Widget.html @@ -1 +1,9 @@

i am a widget

+ + diff --git a/test/generator/component-ref/_config.js b/test/generator/component-ref/_config.js index 09ce6f0f8870..25749f58d925 100644 --- a/test/generator/component-ref/_config.js +++ b/test/generator/component-ref/_config.js @@ -1,9 +1,7 @@ -import Widget from './Widget.html'; - export default { html: '

i am a widget

', test ( assert, component ) { const widget = component.refs.widget; - assert.ok( widget instanceof Widget ); + assert.ok( widget.isWidget ); } };