UNPKG

5.14 kBJavaScriptView Raw
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const util = require("util");
9const ExternalModule = require("./ExternalModule");
10
11/** @typedef {import("../declarations/WebpackOptions").Externals} Externals */
12/** @typedef {import("./NormalModuleFactory")} NormalModuleFactory */
13
14const UNSPECIFIED_EXTERNAL_TYPE_REGEXP = /^[a-z0-9]+ /;
15
16// TODO webpack 6 remove this
17const callDeprecatedExternals = util.deprecate(
18 (externalsFunction, context, request, cb) => {
19 externalsFunction.call(null, context, request, cb);
20 },
21 "The externals-function should be defined like ({context, request}, cb) => { ... }",
22 "DEP_WEBPACK_EXTERNALS_FUNCTION_PARAMETERS"
23);
24
25class ExternalModuleFactoryPlugin {
26 /**
27 * @param {string | undefined} type default external type
28 * @param {Externals} externals externals config
29 */
30 constructor(type, externals) {
31 this.type = type;
32 this.externals = externals;
33 }
34
35 /**
36 * @param {NormalModuleFactory} normalModuleFactory the normal module factory
37 * @returns {void}
38 */
39 apply(normalModuleFactory) {
40 const globalType = this.type;
41 normalModuleFactory.hooks.factorize.tapAsync(
42 "ExternalModuleFactoryPlugin",
43 (data, callback) => {
44 const context = data.context;
45 const dependency = data.dependencies[0];
46
47 /**
48 * @param {string|string[]|boolean|Record<string, string|string[]>} value the external config
49 * @param {string|undefined} type type of external
50 * @param {function(Error=, ExternalModule=): void} callback callback
51 * @returns {void}
52 */
53 const handleExternal = (value, type, callback) => {
54 if (value === false) {
55 // Not externals, fallback to original factory
56 return callback();
57 }
58 /** @type {string | string[] | Record<string, string|string[]>} */
59 let externalConfig;
60 if (value === true) {
61 externalConfig = dependency.request;
62 } else {
63 externalConfig = value;
64 }
65 // When no explicit type is specified, extract it from the externalConfig
66 if (type === undefined) {
67 if (
68 typeof externalConfig === "string" &&
69 UNSPECIFIED_EXTERNAL_TYPE_REGEXP.test(externalConfig)
70 ) {
71 const idx = externalConfig.indexOf(" ");
72 type = externalConfig.substr(0, idx);
73 externalConfig = externalConfig.substr(idx + 1);
74 } else if (
75 Array.isArray(externalConfig) &&
76 externalConfig.length > 0 &&
77 UNSPECIFIED_EXTERNAL_TYPE_REGEXP.test(externalConfig[0])
78 ) {
79 const firstItem = externalConfig[0];
80 const idx = firstItem.indexOf(" ");
81 type = firstItem.substr(0, idx);
82 externalConfig = [
83 firstItem.substr(idx + 1),
84 ...externalConfig.slice(1)
85 ];
86 }
87 }
88 callback(
89 null,
90 new ExternalModule(
91 externalConfig,
92 type || globalType,
93 dependency.request
94 )
95 );
96 };
97
98 /**
99 * @param {Externals} externals externals config
100 * @param {function(Error=, ExternalModule=): void} callback callback
101 * @returns {void}
102 */
103 const handleExternals = (externals, callback) => {
104 if (typeof externals === "string") {
105 if (externals === dependency.request) {
106 return handleExternal(dependency.request, undefined, callback);
107 }
108 } else if (Array.isArray(externals)) {
109 let i = 0;
110 const next = () => {
111 let asyncFlag;
112 const handleExternalsAndCallback = (err, module) => {
113 if (err) return callback(err);
114 if (!module) {
115 if (asyncFlag) {
116 asyncFlag = false;
117 return;
118 }
119 return next();
120 }
121 callback(null, module);
122 };
123
124 do {
125 asyncFlag = true;
126 if (i >= externals.length) return callback();
127 handleExternals(externals[i++], handleExternalsAndCallback);
128 } while (!asyncFlag);
129 asyncFlag = false;
130 };
131
132 next();
133 return;
134 } else if (externals instanceof RegExp) {
135 if (externals.test(dependency.request)) {
136 return handleExternal(dependency.request, undefined, callback);
137 }
138 } else if (typeof externals === "function") {
139 const cb = (err, value, type) => {
140 if (err) return callback(err);
141 if (value !== undefined) {
142 handleExternal(value, type, callback);
143 } else {
144 callback();
145 }
146 };
147 if (externals.length === 3) {
148 // TODO webpack 6 remove this
149 callDeprecatedExternals(
150 externals,
151 context,
152 dependency.request,
153 cb
154 );
155 } else {
156 externals(
157 {
158 context,
159 request: dependency.request
160 },
161 cb
162 );
163 }
164 return;
165 } else if (
166 typeof externals === "object" &&
167 Object.prototype.hasOwnProperty.call(externals, dependency.request)
168 ) {
169 return handleExternal(
170 externals[dependency.request],
171 undefined,
172 callback
173 );
174 }
175 callback();
176 };
177
178 handleExternals(this.externals, callback);
179 }
180 );
181 }
182}
183module.exports = ExternalModuleFactoryPlugin;