From b21fecfe9fc2c7df6a9936d8a555569e75ad230b Mon Sep 17 00:00:00 2001 From: Steve Gill Date: Tue, 12 Jun 2018 16:25:29 -0700 Subject: [PATCH 1/2] added html importing from js file --- src/Bundler.js | 4 ++++ src/builtins/loaders/browser/html-loader.js | 5 +++++ src/builtins/loaders/node/html-loader.js | 20 ++++++++++++++++++ src/packagers/JSPackager.js | 16 ++++++++++++-- test/integration/html-css-js-html/100x100.png | Bin 0 -> 255 bytes test/integration/html-css-js-html/icons.svg | 5 +++++ test/integration/html-css-js-html/index.css | 3 +++ test/integration/html-css-js-html/index.html | 18 ++++++++++++++++ test/integration/html-css-js-html/index.js | 3 +++ test/integration/html-css-js-html/other.html | 10 +++++++++ 10 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 src/builtins/loaders/browser/html-loader.js create mode 100644 src/builtins/loaders/node/html-loader.js create mode 100644 test/integration/html-css-js-html/100x100.png create mode 100644 test/integration/html-css-js-html/icons.svg create mode 100644 test/integration/html-css-js-html/index.css create mode 100644 test/integration/html-css-js-html/index.html create mode 100644 test/integration/html-css-js-html/index.js create mode 100644 test/integration/html-css-js-html/other.html diff --git a/src/Bundler.js b/src/Bundler.js index 5698f5efc52..34711c3dc11 100644 --- a/src/Bundler.js +++ b/src/Bundler.js @@ -52,6 +52,10 @@ class Bundler extends EventEmitter { browser: require.resolve('./builtins/loaders/browser/js-loader'), node: require.resolve('./builtins/loaders/node/js-loader') }); + this.addBundleLoader('html', { + browser: require.resolve('./builtins/loaders/browser/html-loader'), + node: require.resolve('./builtins/loaders/node/html-loader') + }) this.pending = false; this.loadedAssets = new Map(); diff --git a/src/builtins/loaders/browser/html-loader.js b/src/builtins/loaders/browser/html-loader.js new file mode 100644 index 00000000000..36a71480421 --- /dev/null +++ b/src/builtins/loaders/browser/html-loader.js @@ -0,0 +1,5 @@ +module.exports = function loadHTMLBundle(bundle) { + return fetch(bundle).then(function (res) { + return res.text(); + }); +}; diff --git a/src/builtins/loaders/node/html-loader.js b/src/builtins/loaders/node/html-loader.js new file mode 100644 index 00000000000..828184404e4 --- /dev/null +++ b/src/builtins/loaders/node/html-loader.js @@ -0,0 +1,20 @@ +var fs = require('fs'); + +module.exports = function loadHTMLBundle(bundle) { + return new Promise(function(resolve, reject) { + fs.readFile(__dirname + bundle, 'utf8', function(err, data) { + if (err) { + reject(err); + } else { + // wait for the next event loop iteration, so we are sure + // the current module is fully loaded + setImmediate(function() { + resolve(data); + }); + } + }); + }) + .then(function(code) { + new Function('', code)(); + }); +}; diff --git a/src/packagers/JSPackager.js b/src/packagers/JSPackager.js index 15c058778be..6fa191f45d4 100644 --- a/src/packagers/JSPackager.js +++ b/src/packagers/JSPackager.js @@ -69,8 +69,20 @@ class JSPackager extends Packager { !this.bundle.assets.has(mod) && (!this.bundle.parentBundle || this.bundle.parentBundle.type !== 'js') ) { - this.externalModules.add(mod); - this.bundleLoaders.add(mod.type); + if(mod.type === "html") { + this.externalModules.add(mod); + this.bundleLoaders.add(mod.type); + + // if your JS file imports an html dependency, the html + // needs to be included in the generated javascript file + await this.writeModule( + mod.id, + `module.exports=${JSON.stringify(mod.generated.html)}` + ); + } else { + this.externalModules.add(mod); + this.bundleLoaders.add(mod.type); + } } } } diff --git a/test/integration/html-css-js-html/100x100.png b/test/integration/html-css-js-html/100x100.png new file mode 100644 index 0000000000000000000000000000000000000000..8a1daa0121d524256c1d1b45ff5e7ed771784c52 GIT binary patch literal 255 zcmeAS@N?(olHy`uVBq!ia0vp^DImVS)*E46%Oq(`s&YU^>_U&7| zc=6GrN4Ia^zG~H~jT<-Ko_$0QsEo6~BeIx*f$sR&2=kJHM z&5N9@w|=XzipNCbb|s-xrJeH)f2E%im~OIrio1Qy&Sj1+>x<+1)+oPni}kGd6Ohy! z>6x1RttNZTv!7LG>?+Ii&zjEnadB}e3778<6P>u`Anzuis~9|8{an^LB{Ts5qZeh6 literal 0 HcmV?d00001 diff --git a/test/integration/html-css-js-html/icons.svg b/test/integration/html-css-js-html/icons.svg new file mode 100644 index 00000000000..21f0a75277c --- /dev/null +++ b/test/integration/html-css-js-html/icons.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/test/integration/html-css-js-html/index.css b/test/integration/html-css-js-html/index.css new file mode 100644 index 00000000000..67ce83e4d09 --- /dev/null +++ b/test/integration/html-css-js-html/index.css @@ -0,0 +1,3 @@ +body { + background: red; +} diff --git a/test/integration/html-css-js-html/index.html b/test/integration/html-css-js-html/index.html new file mode 100644 index 00000000000..c78f36946d5 --- /dev/null +++ b/test/integration/html-css-js-html/index.html @@ -0,0 +1,18 @@ + + + + + + +

Hello world

+

Linking to another page

+ Hash link + Mailto link + Tel link + + + hello world + + + + diff --git a/test/integration/html-css-js-html/index.js b/test/integration/html-css-js-html/index.js new file mode 100644 index 00000000000..3d19afa37c2 --- /dev/null +++ b/test/integration/html-css-js-html/index.js @@ -0,0 +1,3 @@ +alert('Hi'); + +import Page from './other.html'; diff --git a/test/integration/html-css-js-html/other.html b/test/integration/html-css-js-html/other.html new file mode 100644 index 00000000000..36d14dc9d2f --- /dev/null +++ b/test/integration/html-css-js-html/other.html @@ -0,0 +1,10 @@ + + + + + + +

Other page

+ + + From b7f4f9ca6434cf65a102a32f89bd253e806f4f44 Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Sat, 14 Jul 2018 18:59:31 -0700 Subject: [PATCH 2/2] Fix up HTML loader --- src/Bundler.js | 23 ++-- src/builtins/loaders/node/html-loader.js | 3 - src/packagers/HTMLPackager.js | 5 + src/packagers/JSPackager.js | 16 +-- src/packagers/Packager.js | 4 + src/packagers/RawPackager.js | 5 + test/integration/html-css-js-html/icons.svg | 5 - test/integration/html-css-js-html/index.html | 18 --- test/integration/html-css-js-html/index.js | 3 - .../100x100.png | Bin .../index.css | 0 test/integration/import-html-async/index.js | 1 + .../other.html | 0 test/integration/import-html-sync/100x100.png | Bin 0 -> 255 bytes test/integration/import-html-sync/index.css | 3 + test/integration/import-html-sync/index.js | 1 + test/integration/import-html-sync/other.html | 10 ++ test/javascript.js | 126 +++++++++++++++++- test/utils.js | 5 + 19 files changed, 175 insertions(+), 53 deletions(-) delete mode 100644 test/integration/html-css-js-html/icons.svg delete mode 100644 test/integration/html-css-js-html/index.html delete mode 100644 test/integration/html-css-js-html/index.js rename test/integration/{html-css-js-html => import-html-async}/100x100.png (100%) rename test/integration/{html-css-js-html => import-html-async}/index.css (100%) create mode 100644 test/integration/import-html-async/index.js rename test/integration/{html-css-js-html => import-html-async}/other.html (100%) create mode 100644 test/integration/import-html-sync/100x100.png create mode 100644 test/integration/import-html-sync/index.css create mode 100644 test/integration/import-html-sync/index.js create mode 100644 test/integration/import-html-sync/other.html diff --git a/src/Bundler.js b/src/Bundler.js index c670c89e093..f50241119f0 100644 --- a/src/Bundler.js +++ b/src/Bundler.js @@ -54,7 +54,7 @@ class Bundler extends EventEmitter { this.addBundleLoader('html', { browser: require.resolve('./builtins/loaders/browser/html-loader'), node: require.resolve('./builtins/loaders/node/html-loader') - }) + }); this.pending = false; this.loadedAssets = new Map(); @@ -620,6 +620,12 @@ class Bundler extends EventEmitter { let isEntryAsset = asset.parentBundle && asset.parentBundle.entryAsset === asset; + // If the asset generated a representation for the parent bundle type, and this + // is not an async import, add it to the current bundle + if (bundle.type && asset.generated[bundle.type] != null && !dep.dynamic) { + bundle.addAsset(asset); + } + if ((dep && dep.dynamic) || !bundle.type) { // If the asset is already the entry asset of a bundle, don't create a duplicate. if (isEntryAsset) { @@ -628,24 +634,23 @@ class Bundler extends EventEmitter { // Create a new bundle for dynamic imports bundle = bundle.createChildBundle(asset, dep); - } else if (asset.type && !this.packagers.has(asset.type)) { + } else if ( + asset.type && + !this.packagers.get(asset.type).shouldAddAsset(bundle, asset) + ) { // If the asset is already the entry asset of a bundle, don't create a duplicate. if (isEntryAsset) { return; } - // No packager is available for this asset type. Create a new bundle with only this asset. - bundle.createSiblingBundle(asset); + // No packager is available for this asset type, or the packager doesn't support + // combining this asset into the bundle. Create a new bundle with only this asset. + bundle = bundle.createSiblingBundle(asset, dep); } else { // Add the asset to the common bundle of the asset's type bundle.getSiblingBundle(asset.type).addAsset(asset); } - // If the asset generated a representation for the parent bundle type, also add it there - if (asset.generated[bundle.type] != null) { - bundle.addAsset(asset); - } - // Add the asset to sibling bundles for each generated type if (asset.type && asset.generated[asset.type]) { for (let t in asset.generated) { diff --git a/src/builtins/loaders/node/html-loader.js b/src/builtins/loaders/node/html-loader.js index 828184404e4..64f6f958be6 100644 --- a/src/builtins/loaders/node/html-loader.js +++ b/src/builtins/loaders/node/html-loader.js @@ -13,8 +13,5 @@ module.exports = function loadHTMLBundle(bundle) { }); } }); - }) - .then(function(code) { - new Function('', code)(); }); }; diff --git a/src/packagers/HTMLPackager.js b/src/packagers/HTMLPackager.js index 498ba7dda9d..fdd5364e505 100644 --- a/src/packagers/HTMLPackager.js +++ b/src/packagers/HTMLPackager.js @@ -16,6 +16,11 @@ const metadataContent = new Set([ ]); class HTMLPackager extends Packager { + static shouldAddAsset() { + // We cannot combine multiple HTML files together - they should be written as separate bundles. + return false; + } + async addAsset(asset) { let html = asset.generated.html || ''; diff --git a/src/packagers/JSPackager.js b/src/packagers/JSPackager.js index 60092eb6026..ed72b808f8a 100644 --- a/src/packagers/JSPackager.js +++ b/src/packagers/JSPackager.js @@ -71,20 +71,8 @@ class JSPackager extends Packager { !this.bundle.assets.has(mod) && (!this.bundle.parentBundle || this.bundle.parentBundle.type !== 'js') ) { - if(mod.type === "html") { - this.externalModules.add(mod); - this.bundleLoaders.add(mod.type); - - // if your JS file imports an html dependency, the html - // needs to be included in the generated javascript file - await this.writeModule( - mod.id, - `module.exports=${JSON.stringify(mod.generated.html)}` - ); - } else { - this.externalModules.add(mod); - this.bundleLoaders.add(mod.type); - } + this.externalModules.add(mod); + this.bundleLoaders.add(mod.type); } } } diff --git a/src/packagers/Packager.js b/src/packagers/Packager.js index a9aa82d8bd8..c60dfb7fd4a 100644 --- a/src/packagers/Packager.js +++ b/src/packagers/Packager.js @@ -10,6 +10,10 @@ class Packager { this.options = bundler.options; } + static shouldAddAsset() { + return true; + } + async setup() { // Create sub-directories if needed if (this.bundle.name.includes(path.sep)) { diff --git a/src/packagers/RawPackager.js b/src/packagers/RawPackager.js index cc40ff971db..06677f38b04 100644 --- a/src/packagers/RawPackager.js +++ b/src/packagers/RawPackager.js @@ -3,6 +3,11 @@ const path = require('path'); const fs = require('../utils/fs'); class RawPackager extends Packager { + static shouldAddAsset() { + // We cannot combine multiple raw assets together - they should be written as separate bundles. + return false; + } + // Override so we don't create a file for this bundle. // Each asset will be emitted as a separate file instead. setup() {} diff --git a/test/integration/html-css-js-html/icons.svg b/test/integration/html-css-js-html/icons.svg deleted file mode 100644 index 21f0a75277c..00000000000 --- a/test/integration/html-css-js-html/icons.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/test/integration/html-css-js-html/index.html b/test/integration/html-css-js-html/index.html deleted file mode 100644 index c78f36946d5..00000000000 --- a/test/integration/html-css-js-html/index.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - -

Hello world

-

Linking to another page

- Hash link - Mailto link - Tel link - - - hello world - - - - diff --git a/test/integration/html-css-js-html/index.js b/test/integration/html-css-js-html/index.js deleted file mode 100644 index 3d19afa37c2..00000000000 --- a/test/integration/html-css-js-html/index.js +++ /dev/null @@ -1,3 +0,0 @@ -alert('Hi'); - -import Page from './other.html'; diff --git a/test/integration/html-css-js-html/100x100.png b/test/integration/import-html-async/100x100.png similarity index 100% rename from test/integration/html-css-js-html/100x100.png rename to test/integration/import-html-async/100x100.png diff --git a/test/integration/html-css-js-html/index.css b/test/integration/import-html-async/index.css similarity index 100% rename from test/integration/html-css-js-html/index.css rename to test/integration/import-html-async/index.css diff --git a/test/integration/import-html-async/index.js b/test/integration/import-html-async/index.js new file mode 100644 index 00000000000..7235f4c3023 --- /dev/null +++ b/test/integration/import-html-async/index.js @@ -0,0 +1 @@ +module.exports = import('./other.html'); diff --git a/test/integration/html-css-js-html/other.html b/test/integration/import-html-async/other.html similarity index 100% rename from test/integration/html-css-js-html/other.html rename to test/integration/import-html-async/other.html diff --git a/test/integration/import-html-sync/100x100.png b/test/integration/import-html-sync/100x100.png new file mode 100644 index 0000000000000000000000000000000000000000..8a1daa0121d524256c1d1b45ff5e7ed771784c52 GIT binary patch literal 255 zcmeAS@N?(olHy`uVBq!ia0vp^DImVS)*E46%Oq(`s&YU^>_U&7| zc=6GrN4Ia^zG~H~jT<-Ko_$0QsEo6~BeIx*f$sR&2=kJHM z&5N9@w|=XzipNCbb|s-xrJeH)f2E%im~OIrio1Qy&Sj1+>x<+1)+oPni}kGd6Ohy! z>6x1RttNZTv!7LG>?+Ii&zjEnadB}e3778<6P>u`Anzuis~9|8{an^LB{Ts5qZeh6 literal 0 HcmV?d00001 diff --git a/test/integration/import-html-sync/index.css b/test/integration/import-html-sync/index.css new file mode 100644 index 00000000000..67ce83e4d09 --- /dev/null +++ b/test/integration/import-html-sync/index.css @@ -0,0 +1,3 @@ +body { + background: red; +} diff --git a/test/integration/import-html-sync/index.js b/test/integration/import-html-sync/index.js new file mode 100644 index 00000000000..e01db990d4a --- /dev/null +++ b/test/integration/import-html-sync/index.js @@ -0,0 +1 @@ +output(require('./other.html')); diff --git a/test/integration/import-html-sync/other.html b/test/integration/import-html-sync/other.html new file mode 100644 index 00000000000..36d14dc9d2f --- /dev/null +++ b/test/integration/import-html-sync/other.html @@ -0,0 +1,10 @@ + + + + + + +

Other page

+ + + diff --git a/test/javascript.js b/test/javascript.js index befa3ec47d3..195c853dff1 100644 --- a/test/javascript.js +++ b/test/javascript.js @@ -1,7 +1,7 @@ const assert = require('assert'); const fs = require('../src/utils/fs'); const path = require('path'); -const {bundle, run, assertBundleTree} = require('./utils'); +const {bundle, run, assertBundleTree, deferred} = require('./utils'); const {mkdirp} = require('../src/utils/fs'); describe('javascript', function() { @@ -1140,4 +1140,128 @@ describe('javascript', function() { let module = await run(b); assert.equal(module.default, 'Hello Hello!'); }); + + it('should support importing HTML from JS async', async function() { + let b = await bundle( + __dirname + '/integration/import-html-async/index.js', + {sourceMaps: false} + ); + + await assertBundleTree(b, { + name: 'index.js', + assets: [ + 'index.js', + 'bundle-loader.js', + 'bundle-url.js', + 'html-loader.js' + ], + childBundles: [ + { + type: 'html', + assets: ['other.html'], + childBundles: [ + { + type: 'png', + assets: ['100x100.png'], + childBundles: [] + }, + { + type: 'css', + assets: ['index.css'], + childBundles: [] + } + ] + } + ] + }); + + let output = await run(b); + assert.equal(typeof output, 'string'); + assert(output.includes('')); + assert(output.includes('Other page')); + }); + + it('should support importing HTML from JS async with --target=node', async function() { + let b = await bundle( + __dirname + '/integration/import-html-async/index.js', + { + target: 'node', + sourceMaps: false + } + ); + + await assertBundleTree(b, { + name: 'index.js', + assets: [ + 'index.js', + 'bundle-loader.js', + 'bundle-url.js', + 'html-loader.js' + ], + childBundles: [ + { + type: 'html', + assets: ['other.html'], + childBundles: [ + { + type: 'png', + assets: ['100x100.png'], + childBundles: [] + }, + { + type: 'css', + assets: ['index.css'], + childBundles: [] + } + ] + } + ] + }); + + let output = await run(b); + assert.equal(typeof output, 'string'); + assert(output.includes('')); + assert(output.includes('Other page')); + }); + + it('should support importing HTML from JS sync', async function() { + let b = await bundle(__dirname + '/integration/import-html-sync/index.js', { + sourceMaps: false + }); + + await assertBundleTree(b, { + name: 'index.js', + assets: [ + 'index.js', + 'bundle-loader.js', + 'bundle-url.js', + 'html-loader.js' + ], + childBundles: [ + { + type: 'html', + assets: ['other.html'], + childBundles: [ + { + type: 'png', + assets: ['100x100.png'], + childBundles: [] + }, + { + type: 'css', + assets: ['index.css'], + childBundles: [] + } + ] + } + ] + }); + + let promise = deferred(); + await run(b, {output: promise.resolve}, {require: false}); + let output = await promise; + assert.equal(typeof output, 'string'); + assert(output.includes('')); + assert(output.includes('Other page')); + }); }); diff --git a/test/utils.js b/test/utils.js index a0cd065248a..fd65563cc47 100644 --- a/test/utils.js +++ b/test/utils.js @@ -106,6 +106,11 @@ function prepareBrowserContext(bundle, globals) { nodeFS.readFileSync(path.join(__dirname, 'dist', url)) ).buffer ); + }, + text() { + return Promise.resolve( + nodeFS.readFileSync(path.join(__dirname, 'dist', url), 'utf8') + ); } }); }