Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "webpack-external-import",
"description": "dynamic import() external urls!",
"version": "0.0.1-beta.23",
"version": "0.0.1-beta.32",
"repository": {
"type": "git",
"url": "https://github.com/ScriptedAlchemy/webpack-external-import.git"
Expand All @@ -12,7 +12,8 @@
"babel",
"corsImport.js",
"index.js",
"polyfill.js"
"polyfill.js",
"react.js"
],
"keywords": [
"import url",
Expand Down
9 changes: 9 additions & 0 deletions src/corsImport.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import loadjs from 'loadjs';

const corsImport = (url) => {
if (!url) {
return new Promise((resolve, reject) => reject('no url in corsImport'));
}
if (loadjs.isDefined(url)) {
return new Promise((resolve, reject) => {
resolve();
});
}

loadjs(url, url);

return new Promise((resolve, reject) => {
Expand Down
1 change: 0 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import corsImport from './corsImport';
import ExternalComponent from './react';



export {
corsImport,
ExternalComponent,
Expand Down
8 changes: 7 additions & 1 deletion src/react.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import './polyfill';

class ExternalComponent extends Component {
constructor(props) {
Expand All @@ -14,6 +13,11 @@ class ExternalComponent extends Component {
}

importPromise(src) {
if (!src) {
return new Promise((resolve, reject) => {
reject();
});
}
if (this.props.cors) {
return require('./corsImport').default(src);
}
Expand All @@ -24,6 +28,8 @@ class ExternalComponent extends Component {
}

componentDidMount() {
require('./polyfill');

const { src, module, export: exportName } = this.props;
if (!src) {
throw new Error(`dynamic-import: no url ${JSON.stringify(this.props, null, 2)}`);
Expand Down
185 changes: 101 additions & 84 deletions src/webpack/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,26 +34,29 @@ const emitCountMap = new Map();
class URLImportPlugin {
constructor(opts) {
if (!opts.manifestName) {
throw new Error('URLImportPlugin: You MUST specify a manifestName in your options. Something unique. Like {manifestName: my-special-build}');
throw new Error(
'URLImportPlugin: You MUST specify a manifestName in your options. Something unique. Like {manifestName: my-special-build}',
);
}
this.opts = Object.assign({
publicPath: null,
basePath: '',
manifestName: 'unknown-project',
fileName: 'importManifest.js',
transformExtensions: /^(gz|map)$/i,
writeToFileEmit: false,
seed: null,
filter: null,
map: null,
generate: null,
sort: null,
serialize: manifest => `if(!window.entryManifest) {window.entryManifest = {}}; window.entryManifest["${opts.manifestName}"] = ${JSON.stringify(
manifest,
null,
2,
)}`,
}, opts || {});
this.opts = Object.assign(
{
publicPath: null,
basePath: '',
manifestName: 'unknown-project',
fileName: 'importManifest.js',
transformExtensions: /^(gz|map)$/i,
writeToFileEmit: false,
seed: null,
filter: null,
map: null,
generate: null,
sort: null,
serialize: manifest => `if(!window.entryManifest) {window.entryManifest = {}}; window.entryManifest["${
opts.manifestName
}"] = ${JSON.stringify(manifest, null, 2)}`,
},
opts || {},
);
}

getFileType(str) {
Expand All @@ -72,7 +75,9 @@ class URLImportPlugin {
chunkSplitting.interleave = {
test(module) {
if (module.resource) {
return module.resource.includes('src') && !!hasExternalizedModule(module);
return (
module.resource.includes('src') && !!hasExternalizedModule(module)
);
}
},
name(module, chunks, cacheGroupKey) {
Expand Down Expand Up @@ -111,14 +116,15 @@ class URLImportPlugin {

mergeDeep(options, {
optimization: {
namedModules: true,
splitChunks: {
chunks: options?.optimization?.splitChunks?.chunks || 'all',
cacheGroups: chunkSplitting,
},
},
});

Object.assign(options.optimization.splitChunks, { chunks: 'all' });
Object.assign(options.optimization, { namedModules: true });

const moduleAssets = {};
const externalModules = {};
const outputFolder = compiler.options.output.path;
Expand All @@ -144,29 +150,31 @@ class URLImportPlugin {

const seed = this.opts.seed || {};

const publicPath = this.opts.publicPath != null ? this.opts.publicPath : compilation.options.output.publicPath;
const stats = compilation.getStats()
.toJson();
const publicPath = this.opts.publicPath != null
? this.opts.publicPath
: compilation.options.output.publicPath;
const stats = compilation.getStats().toJson();
// console.log('Webpack Plugin Debugging');

let files = compilation.chunks.reduce((files, chunk) => chunk.files.reduce((files, path) => {
let name = chunk.name ? chunk.name : null;
let files = compilation.chunks.reduce(
(files, chunk) => chunk.files.reduce((files, path) => {
let name = chunk.name ? chunk.name : null;

if (name) {
name = `${name}.${this.getFileType(path)}`;
} else {
// For nameless chunks, just map the files directly.
name = path;
}
if (name) {
name = `${name}.${this.getFileType(path)}`;
} else {
// For nameless chunks, just map the files directly.
name = path;
}

// console.log('stats', stats);
// console.log('stats', stats);

if (externalModules[chunk.id]) {
if (externalModules[chunk.id]) {
// TODO: swap forEachModle out with const of
// const module of chunk.modulesIterable
chunk.forEachModule((module) => {
if (module.dependencies) {
module.dependencies.forEach((dependency) => {
chunk.forEachModule((module) => {
if (module.dependencies) {
module.dependencies.forEach((dependency) => {
// console.group();
// console.log('dependencies foreach: dependency.module', dependency.module);
// console.log('dependencies foreach: dependency.module.entry', dependency?.module?.entry?.());
Expand All @@ -176,39 +184,40 @@ class URLImportPlugin {
// console.groupEnd();
// console.log("Back to level 2");
// console.groupEnd();
const dependencyModuleSet = dependency.getReference?.()?.module;
if (!dependencyModuleSet) return null;
const dependencyModuleSet = dependency.getReference?.()?.module;
if (!dependencyModuleSet) return null;
// console.log('getReference chunks', dependencyModuleSet);
// console.log('dependencyModuleSet', dependencyModuleSet);
// console.log('dependencyModuleSet entryModule', dependencyModuleSet?.entryModule?.());
// for (const module of dependencyModuleSet.chunksIterable) {
// console.log('iterated dependency module', module);
// console.log(module.block);
// }
});
}
});
}
});
}

// Webpack 4: .isOnlyInitial()
// Webpack 3: .isInitial()
// Webpack 1/2: .initial
// const modules = chunk.modulesIterable;
// let i = 0;
// while (i < modules.length) {
// getMeta(modules[i]);
// i++;
// }
return files.concat({
path,
chunk,
name,
isInitial: chunk.isOnlyInitial ? chunk.isOnlyInitial() : (chunk.isInitial ? chunk.isInitial() : chunk.initial),
isChunk: true,
isAsset: false,
isModuleAsset: false,
});
}

// Webpack 4: .isOnlyInitial()
// Webpack 3: .isInitial()
// Webpack 1/2: .initial
// const modules = chunk.modulesIterable;
// let i = 0;
// while (i < modules.length) {
// getMeta(modules[i]);
// i++;
// }
return files.concat({
path,
chunk,
name,
isInitial: chunk.isOnlyInitial ? chunk.isOnlyInitial() : (chunk.isInitial ? chunk.isInitial() : chunk.initial),
isChunk: true,
isAsset: false,
isModuleAsset: false,
});
}, files), []);
}, files), [],
);

// module assets don't show up in assetsByChunkName.
// we're getting them this way;
Expand Down Expand Up @@ -299,13 +308,15 @@ class URLImportPlugin {

const isLastEmit = emitCount === 0;
if (isLastEmit) {
const cleanedManifest = Object.entries(manifest)
.reduce((acc, [key, asset]) => {
const cleanedManifest = Object.entries(manifest).reduce(
(acc, [key, asset]) => {
if (!asset.includes('.map')) {
return Object.assign(acc, { [key]: asset });
}
return acc;
}, {});
},
{},
);

const output = this.opts.serialize(cleanedManifest);

Expand All @@ -326,7 +337,11 @@ class URLImportPlugin {
if (compiler.hooks) {
compiler.hooks.webpackURLImportPluginAfterEmit.call(manifest);
} else {
compilation.applyPluginsAsync('webpack-manifest-plugin-after-emit', manifest, compileCallback);
compilation.applyPluginsAsync(
'webpack-manifest-plugin-after-emit',
manifest,
compileCallback,
);
}
};

Expand All @@ -345,26 +360,28 @@ class URLImportPlugin {
name: 'URLImportPlugin',
stage: Infinity,
};
compiler.hooks.webpackURLImportPluginAfterEmit = new SyncWaterfallHook(['manifest']);
compiler.hooks.webpackURLImportPluginAfterEmit = new SyncWaterfallHook([
'manifest',
]);

compiler.hooks.compilation.tap('URLImportPlugin', (compilation) => {
compilation.hooks.beforeModuleIds.tap(
'URLImportPlugin',
(modules) => {
for (const module of modules) {
const moduleSource = module?.originalSource?.().source?.() || '';
if (moduleSource?.indexOf('externalize') > -1 || false) {
module.buildMeta = mergeDeep(module.buildMeta, { isExternalized: true });
try {
// look at refactoring this to use buildMeta not mutate id
module.id = moduleSource.match(/\/\*\s*externalize\s*:\s*(\S+)\s*\*\//)[1];
} catch (error) {
}
externalModules[module.id] = {};
}
compilation.hooks.beforeModuleIds.tap('URLImportPlugin', (modules) => {
for (const module of modules) {
const moduleSource = module?.originalSource?.().source?.() || '';
if (moduleSource?.indexOf('externalize') > -1 || false) {
module.buildMeta = mergeDeep(module.buildMeta, {
isExternalized: true,
});
try {
// look at refactoring this to use buildMeta not mutate id
module.id = moduleSource.match(
/\/\*\s*externalize\s*:\s*(\S+)\s*\*\//,
)[1];
} catch (error) {}
externalModules[module.id] = {};
}
},
);
}
});
});

compiler.hooks.compilation.tap(pluginOptions, ({ hooks }) => {
Expand Down