[79a0317] | 1 | /*
|
---|
| 2 | MIT License http://www.opensource.org/licenses/mit-license.php
|
---|
| 3 | Author Tobias Koppers @sokra
|
---|
| 4 | */
|
---|
| 5 |
|
---|
| 6 | "use strict";
|
---|
| 7 |
|
---|
| 8 | const memoize = require("../util/memoize");
|
---|
| 9 |
|
---|
| 10 | const getBrowserslistTargetHandler = memoize(() =>
|
---|
| 11 | require("./browserslistTargetHandler")
|
---|
| 12 | );
|
---|
| 13 |
|
---|
| 14 | /**
|
---|
| 15 | * @param {string} context the context directory
|
---|
| 16 | * @returns {string} default target
|
---|
| 17 | */
|
---|
| 18 | const getDefaultTarget = context => {
|
---|
| 19 | const browsers = getBrowserslistTargetHandler().load(null, context);
|
---|
| 20 | return browsers ? "browserslist" : "web";
|
---|
| 21 | };
|
---|
| 22 |
|
---|
| 23 | /**
|
---|
| 24 | * @typedef {object} PlatformTargetProperties
|
---|
| 25 | * @property {boolean | null} web web platform, importing of http(s) and std: is available
|
---|
| 26 | * @property {boolean | null} browser browser platform, running in a normal web browser
|
---|
| 27 | * @property {boolean | null} webworker (Web)Worker platform, running in a web/shared/service worker
|
---|
| 28 | * @property {boolean | null} node node platform, require of node built-in modules is available
|
---|
| 29 | * @property {boolean | null} nwjs nwjs platform, require of legacy nw.gui is available
|
---|
| 30 | * @property {boolean | null} electron electron platform, require of some electron built-in modules is available
|
---|
| 31 | */
|
---|
| 32 |
|
---|
| 33 | /**
|
---|
| 34 | * @typedef {object} ElectronContextTargetProperties
|
---|
| 35 | * @property {boolean | null} electronMain in main context
|
---|
| 36 | * @property {boolean | null} electronPreload in preload context
|
---|
| 37 | * @property {boolean | null} electronRenderer in renderer context with node integration
|
---|
| 38 | */
|
---|
| 39 |
|
---|
| 40 | /**
|
---|
| 41 | * @typedef {object} ApiTargetProperties
|
---|
| 42 | * @property {boolean | null} require has require function available
|
---|
| 43 | * @property {boolean | null} nodeBuiltins has node.js built-in modules available
|
---|
| 44 | * @property {boolean | null} nodePrefixForCoreModules node.js allows to use `node:` prefix for core modules
|
---|
| 45 | * @property {boolean | null} document has document available (allows script tags)
|
---|
| 46 | * @property {boolean | null} importScripts has importScripts available
|
---|
| 47 | * @property {boolean | null} importScriptsInWorker has importScripts available when creating a worker
|
---|
| 48 | * @property {boolean | null} fetchWasm has fetch function available for WebAssembly
|
---|
| 49 | * @property {boolean | null} global has global variable available
|
---|
| 50 | */
|
---|
| 51 |
|
---|
| 52 | /**
|
---|
| 53 | * @typedef {object} EcmaTargetProperties
|
---|
| 54 | * @property {boolean | null} globalThis has globalThis variable available
|
---|
| 55 | * @property {boolean | null} bigIntLiteral big int literal syntax is available
|
---|
| 56 | * @property {boolean | null} const const and let variable declarations are available
|
---|
| 57 | * @property {boolean | null} arrowFunction arrow functions are available
|
---|
| 58 | * @property {boolean | null} forOf for of iteration is available
|
---|
| 59 | * @property {boolean | null} destructuring destructuring is available
|
---|
| 60 | * @property {boolean | null} dynamicImport async import() is available
|
---|
| 61 | * @property {boolean | null} dynamicImportInWorker async import() is available when creating a worker
|
---|
| 62 | * @property {boolean | null} module ESM syntax is available (when in module)
|
---|
| 63 | * @property {boolean | null} optionalChaining optional chaining is available
|
---|
| 64 | * @property {boolean | null} templateLiteral template literal is available
|
---|
| 65 | * @property {boolean | null} asyncFunction async functions and await are available
|
---|
| 66 | */
|
---|
| 67 |
|
---|
| 68 | /**
|
---|
| 69 | * @template T
|
---|
| 70 | * @typedef {{ [P in keyof T]?: never }} Never<T>
|
---|
| 71 | */
|
---|
| 72 |
|
---|
| 73 | /**
|
---|
| 74 | * @template A
|
---|
| 75 | * @template B
|
---|
| 76 | * @typedef {(A & Never<B>) | (Never<A> & B) | (A & B)} Mix<A, B>
|
---|
| 77 | */
|
---|
| 78 |
|
---|
| 79 | /** @typedef {Mix<Mix<PlatformTargetProperties, ElectronContextTargetProperties>, Mix<ApiTargetProperties, EcmaTargetProperties>>} TargetProperties */
|
---|
| 80 |
|
---|
| 81 | /**
|
---|
| 82 | * @param {string} major major version
|
---|
| 83 | * @param {string | undefined} minor minor version
|
---|
| 84 | * @returns {(vMajor: number, vMinor?: number) => boolean | undefined} check if version is greater or equal
|
---|
| 85 | */
|
---|
| 86 | const versionDependent = (major, minor) => {
|
---|
| 87 | if (!major) {
|
---|
| 88 | return () => /** @type {undefined} */ (undefined);
|
---|
| 89 | }
|
---|
| 90 | /** @type {number} */
|
---|
| 91 | const nMajor = Number(major);
|
---|
| 92 | /** @type {number} */
|
---|
| 93 | const nMinor = minor ? Number(minor) : 0;
|
---|
| 94 | return (vMajor, vMinor = 0) =>
|
---|
| 95 | nMajor > vMajor || (nMajor === vMajor && nMinor >= vMinor);
|
---|
| 96 | };
|
---|
| 97 |
|
---|
| 98 | /** @type {[string, string, RegExp, (...args: string[]) => Partial<TargetProperties>][]} */
|
---|
| 99 | const TARGETS = [
|
---|
| 100 | [
|
---|
| 101 | "browserslist / browserslist:env / browserslist:query / browserslist:path-to-config / browserslist:path-to-config:env",
|
---|
| 102 | "Resolve features from browserslist. Will resolve browserslist config automatically. Only browser or node queries are supported (electron is not supported). Examples: 'browserslist:modern' to use 'modern' environment from browserslist config",
|
---|
| 103 | /^browserslist(?::(.+))?$/,
|
---|
| 104 | (rest, context) => {
|
---|
| 105 | const browserslistTargetHandler = getBrowserslistTargetHandler();
|
---|
| 106 | const browsers = browserslistTargetHandler.load(
|
---|
| 107 | rest ? rest.trim() : null,
|
---|
| 108 | context
|
---|
| 109 | );
|
---|
| 110 | if (!browsers) {
|
---|
| 111 | throw new Error(`No browserslist config found to handle the 'browserslist' target.
|
---|
| 112 | See https://github.com/browserslist/browserslist#queries for possible ways to provide a config.
|
---|
| 113 | The recommended way is to add a 'browserslist' key to your package.json and list supported browsers (resp. node.js versions).
|
---|
| 114 | You can also more options via the 'target' option: 'browserslist' / 'browserslist:env' / 'browserslist:query' / 'browserslist:path-to-config' / 'browserslist:path-to-config:env'`);
|
---|
| 115 | }
|
---|
| 116 |
|
---|
| 117 | return browserslistTargetHandler.resolve(browsers);
|
---|
| 118 | }
|
---|
| 119 | ],
|
---|
| 120 | [
|
---|
| 121 | "web",
|
---|
| 122 | "Web browser.",
|
---|
| 123 | /^web$/,
|
---|
| 124 | () => ({
|
---|
| 125 | node: false,
|
---|
| 126 | web: true,
|
---|
| 127 | webworker: null,
|
---|
| 128 | browser: true,
|
---|
| 129 | electron: false,
|
---|
| 130 | nwjs: false,
|
---|
| 131 |
|
---|
| 132 | document: true,
|
---|
| 133 | importScriptsInWorker: true,
|
---|
| 134 | fetchWasm: true,
|
---|
| 135 | nodeBuiltins: false,
|
---|
| 136 | importScripts: false,
|
---|
| 137 | require: false,
|
---|
| 138 | global: false
|
---|
| 139 | })
|
---|
| 140 | ],
|
---|
| 141 | [
|
---|
| 142 | "webworker",
|
---|
| 143 | "Web Worker, SharedWorker or Service Worker.",
|
---|
| 144 | /^webworker$/,
|
---|
| 145 | () => ({
|
---|
| 146 | node: false,
|
---|
| 147 | web: true,
|
---|
| 148 | webworker: true,
|
---|
| 149 | browser: true,
|
---|
| 150 | electron: false,
|
---|
| 151 | nwjs: false,
|
---|
| 152 |
|
---|
| 153 | importScripts: true,
|
---|
| 154 | importScriptsInWorker: true,
|
---|
| 155 | fetchWasm: true,
|
---|
| 156 | nodeBuiltins: false,
|
---|
| 157 | require: false,
|
---|
| 158 | document: false,
|
---|
| 159 | global: false
|
---|
| 160 | })
|
---|
| 161 | ],
|
---|
| 162 | [
|
---|
| 163 | "[async-]node[X[.Y]]",
|
---|
| 164 | "Node.js in version X.Y. The 'async-' prefix will load chunks asynchronously via 'fs' and 'vm' instead of 'require()'. Examples: node14.5, async-node10.",
|
---|
| 165 | /^(async-)?node((\d+)(?:\.(\d+))?)?$/,
|
---|
| 166 | (asyncFlag, _, major, minor) => {
|
---|
| 167 | const v = versionDependent(major, minor);
|
---|
| 168 | // see https://node.green/
|
---|
| 169 | return {
|
---|
| 170 | node: true,
|
---|
| 171 | web: false,
|
---|
| 172 | webworker: false,
|
---|
| 173 | browser: false,
|
---|
| 174 | electron: false,
|
---|
| 175 | nwjs: false,
|
---|
| 176 |
|
---|
| 177 | require: !asyncFlag,
|
---|
| 178 | nodeBuiltins: true,
|
---|
| 179 | // v16.0.0, v14.18.0
|
---|
| 180 | nodePrefixForCoreModules: Number(major) < 15 ? v(14, 18) : v(16),
|
---|
| 181 | global: true,
|
---|
| 182 | document: false,
|
---|
| 183 | fetchWasm: false,
|
---|
| 184 | importScripts: false,
|
---|
| 185 | importScriptsInWorker: false,
|
---|
| 186 |
|
---|
| 187 | globalThis: v(12),
|
---|
| 188 | const: v(6),
|
---|
| 189 | templateLiteral: v(4),
|
---|
| 190 | optionalChaining: v(14),
|
---|
| 191 | arrowFunction: v(6),
|
---|
| 192 | asyncFunction: v(7, 6),
|
---|
| 193 | forOf: v(5),
|
---|
| 194 | destructuring: v(6),
|
---|
| 195 | bigIntLiteral: v(10, 4),
|
---|
| 196 | dynamicImport: v(12, 17),
|
---|
| 197 | dynamicImportInWorker: major ? false : undefined,
|
---|
| 198 | module: v(12, 17)
|
---|
| 199 | };
|
---|
| 200 | }
|
---|
| 201 | ],
|
---|
| 202 | [
|
---|
| 203 | "electron[X[.Y]]-main/preload/renderer",
|
---|
| 204 | "Electron in version X.Y. Script is running in main, preload resp. renderer context.",
|
---|
| 205 | /^electron((\d+)(?:\.(\d+))?)?-(main|preload|renderer)$/,
|
---|
| 206 | (_, major, minor, context) => {
|
---|
| 207 | const v = versionDependent(major, minor);
|
---|
| 208 | // see https://node.green/ + https://github.com/electron/releases
|
---|
| 209 | return {
|
---|
| 210 | node: true,
|
---|
| 211 | web: context !== "main",
|
---|
| 212 | webworker: false,
|
---|
| 213 | browser: false,
|
---|
| 214 | electron: true,
|
---|
| 215 | nwjs: false,
|
---|
| 216 |
|
---|
| 217 | electronMain: context === "main",
|
---|
| 218 | electronPreload: context === "preload",
|
---|
| 219 | electronRenderer: context === "renderer",
|
---|
| 220 |
|
---|
| 221 | global: true,
|
---|
| 222 | nodeBuiltins: true,
|
---|
| 223 | // 15.0.0 - Node.js v16.5
|
---|
| 224 | // 14.0.0 - Mode.js v14.17, but prefixes only since v14.18
|
---|
| 225 | nodePrefixForCoreModules: v(15),
|
---|
| 226 |
|
---|
| 227 | require: true,
|
---|
| 228 | document: context === "renderer",
|
---|
| 229 | fetchWasm: context === "renderer",
|
---|
| 230 | importScripts: false,
|
---|
| 231 | importScriptsInWorker: true,
|
---|
| 232 |
|
---|
| 233 | globalThis: v(5),
|
---|
| 234 | const: v(1, 1),
|
---|
| 235 | templateLiteral: v(1, 1),
|
---|
| 236 | optionalChaining: v(8),
|
---|
| 237 | arrowFunction: v(1, 1),
|
---|
| 238 | asyncFunction: v(1, 7),
|
---|
| 239 | forOf: v(0, 36),
|
---|
| 240 | destructuring: v(1, 1),
|
---|
| 241 | bigIntLiteral: v(4),
|
---|
| 242 | dynamicImport: v(11),
|
---|
| 243 | dynamicImportInWorker: major ? false : undefined,
|
---|
| 244 | module: v(11)
|
---|
| 245 | };
|
---|
| 246 | }
|
---|
| 247 | ],
|
---|
| 248 | [
|
---|
| 249 | "nwjs[X[.Y]] / node-webkit[X[.Y]]",
|
---|
| 250 | "NW.js in version X.Y.",
|
---|
| 251 | /^(?:nwjs|node-webkit)((\d+)(?:\.(\d+))?)?$/,
|
---|
| 252 | (_, major, minor) => {
|
---|
| 253 | const v = versionDependent(major, minor);
|
---|
| 254 | // see https://node.green/ + https://github.com/nwjs/nw.js/blob/nw48/CHANGELOG.md
|
---|
| 255 | return {
|
---|
| 256 | node: true,
|
---|
| 257 | web: true,
|
---|
| 258 | webworker: null,
|
---|
| 259 | browser: false,
|
---|
| 260 | electron: false,
|
---|
| 261 | nwjs: true,
|
---|
| 262 |
|
---|
| 263 | global: true,
|
---|
| 264 | nodeBuiltins: true,
|
---|
| 265 | document: false,
|
---|
| 266 | importScriptsInWorker: false,
|
---|
| 267 | fetchWasm: false,
|
---|
| 268 | importScripts: false,
|
---|
| 269 | require: false,
|
---|
| 270 |
|
---|
| 271 | globalThis: v(0, 43),
|
---|
| 272 | const: v(0, 15),
|
---|
| 273 | templateLiteral: v(0, 13),
|
---|
| 274 | optionalChaining: v(0, 44),
|
---|
| 275 | arrowFunction: v(0, 15),
|
---|
| 276 | asyncFunction: v(0, 21),
|
---|
| 277 | forOf: v(0, 13),
|
---|
| 278 | destructuring: v(0, 15),
|
---|
| 279 | bigIntLiteral: v(0, 32),
|
---|
| 280 | dynamicImport: v(0, 43),
|
---|
| 281 | dynamicImportInWorker: major ? false : undefined,
|
---|
| 282 | module: v(0, 43)
|
---|
| 283 | };
|
---|
| 284 | }
|
---|
| 285 | ],
|
---|
| 286 | [
|
---|
| 287 | "esX",
|
---|
| 288 | "EcmaScript in this version. Examples: es2020, es5.",
|
---|
| 289 | /^es(\d+)$/,
|
---|
| 290 | version => {
|
---|
| 291 | let v = Number(version);
|
---|
| 292 | if (v < 1000) v = v + 2009;
|
---|
| 293 | return {
|
---|
| 294 | const: v >= 2015,
|
---|
| 295 | templateLiteral: v >= 2015,
|
---|
| 296 | optionalChaining: v >= 2020,
|
---|
| 297 | arrowFunction: v >= 2015,
|
---|
| 298 | forOf: v >= 2015,
|
---|
| 299 | destructuring: v >= 2015,
|
---|
| 300 | module: v >= 2015,
|
---|
| 301 | asyncFunction: v >= 2017,
|
---|
| 302 | globalThis: v >= 2020,
|
---|
| 303 | bigIntLiteral: v >= 2020,
|
---|
| 304 | dynamicImport: v >= 2020,
|
---|
| 305 | dynamicImportInWorker: v >= 2020
|
---|
| 306 | };
|
---|
| 307 | }
|
---|
| 308 | ]
|
---|
| 309 | ];
|
---|
| 310 |
|
---|
| 311 | /**
|
---|
| 312 | * @param {string} target the target
|
---|
| 313 | * @param {string} context the context directory
|
---|
| 314 | * @returns {TargetProperties} target properties
|
---|
| 315 | */
|
---|
| 316 | const getTargetProperties = (target, context) => {
|
---|
| 317 | for (const [, , regExp, handler] of TARGETS) {
|
---|
| 318 | const match = regExp.exec(target);
|
---|
| 319 | if (match) {
|
---|
| 320 | const [, ...args] = match;
|
---|
| 321 | const result = handler(...args, context);
|
---|
| 322 | if (result) return /** @type {TargetProperties} */ (result);
|
---|
| 323 | }
|
---|
| 324 | }
|
---|
| 325 | throw new Error(
|
---|
| 326 | `Unknown target '${target}'. The following targets are supported:\n${TARGETS.map(
|
---|
| 327 | ([name, description]) => `* ${name}: ${description}`
|
---|
| 328 | ).join("\n")}`
|
---|
| 329 | );
|
---|
| 330 | };
|
---|
| 331 |
|
---|
| 332 | /**
|
---|
| 333 | * @param {TargetProperties[]} targetProperties array of target properties
|
---|
| 334 | * @returns {TargetProperties} merged target properties
|
---|
| 335 | */
|
---|
| 336 | const mergeTargetProperties = targetProperties => {
|
---|
| 337 | /** @type {Set<keyof TargetProperties>} */
|
---|
| 338 | const keys = new Set();
|
---|
| 339 | for (const tp of targetProperties) {
|
---|
| 340 | for (const key of Object.keys(tp)) {
|
---|
| 341 | keys.add(/** @type {keyof TargetProperties} */ (key));
|
---|
| 342 | }
|
---|
| 343 | }
|
---|
| 344 | /** @type {object} */
|
---|
| 345 | const result = {};
|
---|
| 346 | for (const key of keys) {
|
---|
| 347 | let hasTrue = false;
|
---|
| 348 | let hasFalse = false;
|
---|
| 349 | for (const tp of targetProperties) {
|
---|
| 350 | const value = tp[key];
|
---|
| 351 | switch (value) {
|
---|
| 352 | case true:
|
---|
| 353 | hasTrue = true;
|
---|
| 354 | break;
|
---|
| 355 | case false:
|
---|
| 356 | hasFalse = true;
|
---|
| 357 | break;
|
---|
| 358 | }
|
---|
| 359 | }
|
---|
| 360 | if (hasTrue || hasFalse)
|
---|
| 361 | /** @type {TargetProperties} */
|
---|
| 362 | (result)[key] = hasFalse && hasTrue ? null : Boolean(hasTrue);
|
---|
| 363 | }
|
---|
| 364 | return /** @type {TargetProperties} */ (result);
|
---|
| 365 | };
|
---|
| 366 |
|
---|
| 367 | /**
|
---|
| 368 | * @param {string[]} targets the targets
|
---|
| 369 | * @param {string} context the context directory
|
---|
| 370 | * @returns {TargetProperties} target properties
|
---|
| 371 | */
|
---|
| 372 | const getTargetsProperties = (targets, context) =>
|
---|
| 373 | mergeTargetProperties(targets.map(t => getTargetProperties(t, context)));
|
---|
| 374 |
|
---|
| 375 | module.exports.getDefaultTarget = getDefaultTarget;
|
---|
| 376 | module.exports.getTargetProperties = getTargetProperties;
|
---|
| 377 | module.exports.getTargetsProperties = getTargetsProperties;
|
---|