[6a3a178] | 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} document has document available (allows script tags)
|
---|
| 45 | * @property {boolean | null} importScripts has importScripts available
|
---|
| 46 | * @property {boolean | null} importScriptsInWorker has importScripts available when creating a worker
|
---|
| 47 | * @property {boolean | null} fetchWasm has fetch function available for WebAssembly
|
---|
| 48 | * @property {boolean | null} global has global variable available
|
---|
| 49 | */
|
---|
| 50 |
|
---|
| 51 | /**
|
---|
| 52 | * @typedef {Object} EcmaTargetProperties
|
---|
| 53 | * @property {boolean | null} globalThis has globalThis variable available
|
---|
| 54 | * @property {boolean | null} bigIntLiteral big int literal syntax is available
|
---|
| 55 | * @property {boolean | null} const const and let variable declarations are available
|
---|
| 56 | * @property {boolean | null} arrowFunction arrow functions are available
|
---|
| 57 | * @property {boolean | null} forOf for of iteration is available
|
---|
| 58 | * @property {boolean | null} destructuring destructuring is available
|
---|
| 59 | * @property {boolean | null} dynamicImport async import() is available
|
---|
| 60 | * @property {boolean | null} dynamicImportInWorker async import() is available when creating a worker
|
---|
| 61 | * @property {boolean | null} module ESM syntax is available (when in module)
|
---|
| 62 | */
|
---|
| 63 |
|
---|
| 64 | ///** @typedef {PlatformTargetProperties | ApiTargetProperties | EcmaTargetProperties | PlatformTargetProperties & ApiTargetProperties | PlatformTargetProperties & EcmaTargetProperties | ApiTargetProperties & EcmaTargetProperties} TargetProperties */
|
---|
| 65 | /** @template T @typedef {{ [P in keyof T]?: never }} Never<T> */
|
---|
| 66 | /** @template A @template B @typedef {(A & Never<B>) | (Never<A> & B) | (A & B)} Mix<A,B> */
|
---|
| 67 | /** @typedef {Mix<Mix<PlatformTargetProperties, ElectronContextTargetProperties>, Mix<ApiTargetProperties, EcmaTargetProperties>>} TargetProperties */
|
---|
| 68 |
|
---|
| 69 | const versionDependent = (major, minor) => {
|
---|
| 70 | if (!major) return () => /** @type {undefined} */ (undefined);
|
---|
| 71 | major = +major;
|
---|
| 72 | minor = minor ? +minor : 0;
|
---|
| 73 | return (vMajor, vMinor = 0) => {
|
---|
| 74 | return major > vMajor || (major === vMajor && minor >= vMinor);
|
---|
| 75 | };
|
---|
| 76 | };
|
---|
| 77 |
|
---|
| 78 | /** @type {[string, string, RegExp, (...args: string[]) => TargetProperties | false][]} */
|
---|
| 79 | const TARGETS = [
|
---|
| 80 | [
|
---|
| 81 | "browserslist / browserslist:env / browserslist:query / browserslist:path-to-config / browserslist:path-to-config:env",
|
---|
| 82 | "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",
|
---|
| 83 | /^browserslist(?::(.+))?$/,
|
---|
| 84 | (rest, context) => {
|
---|
| 85 | const browserslistTargetHandler = getBrowserslistTargetHandler();
|
---|
| 86 | const browsers = browserslistTargetHandler.load(
|
---|
| 87 | rest ? rest.trim() : null,
|
---|
| 88 | context
|
---|
| 89 | );
|
---|
| 90 | if (!browsers) {
|
---|
| 91 | throw new Error(`No browserslist config found to handle the 'browserslist' target.
|
---|
| 92 | See https://github.com/browserslist/browserslist#queries for possible ways to provide a config.
|
---|
| 93 | The recommended way is to add a 'browserslist' key to your package.json and list supported browsers (resp. node.js versions).
|
---|
| 94 | You can also more options via the 'target' option: 'browserslist' / 'browserslist:env' / 'browserslist:query' / 'browserslist:path-to-config' / 'browserslist:path-to-config:env'`);
|
---|
| 95 | }
|
---|
| 96 | return browserslistTargetHandler.resolve(browsers);
|
---|
| 97 | }
|
---|
| 98 | ],
|
---|
| 99 | [
|
---|
| 100 | "web",
|
---|
| 101 | "Web browser.",
|
---|
| 102 | /^web$/,
|
---|
| 103 | () => {
|
---|
| 104 | return {
|
---|
| 105 | web: true,
|
---|
| 106 | browser: true,
|
---|
| 107 | webworker: null,
|
---|
| 108 | node: false,
|
---|
| 109 | electron: false,
|
---|
| 110 | nwjs: false,
|
---|
| 111 |
|
---|
| 112 | document: true,
|
---|
| 113 | importScriptsInWorker: true,
|
---|
| 114 | fetchWasm: true,
|
---|
| 115 | nodeBuiltins: false,
|
---|
| 116 | importScripts: false,
|
---|
| 117 | require: false,
|
---|
| 118 | global: false
|
---|
| 119 | };
|
---|
| 120 | }
|
---|
| 121 | ],
|
---|
| 122 | [
|
---|
| 123 | "webworker",
|
---|
| 124 | "Web Worker, SharedWorker or Service Worker.",
|
---|
| 125 | /^webworker$/,
|
---|
| 126 | () => {
|
---|
| 127 | return {
|
---|
| 128 | web: true,
|
---|
| 129 | browser: true,
|
---|
| 130 | webworker: true,
|
---|
| 131 | node: false,
|
---|
| 132 | electron: false,
|
---|
| 133 | nwjs: false,
|
---|
| 134 |
|
---|
| 135 | importScripts: true,
|
---|
| 136 | importScriptsInWorker: true,
|
---|
| 137 | fetchWasm: true,
|
---|
| 138 | nodeBuiltins: false,
|
---|
| 139 | require: false,
|
---|
| 140 | document: false,
|
---|
| 141 | global: false
|
---|
| 142 | };
|
---|
| 143 | }
|
---|
| 144 | ],
|
---|
| 145 | [
|
---|
| 146 | "[async-]node[X[.Y]]",
|
---|
| 147 | "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.",
|
---|
| 148 | /^(async-)?node(\d+(?:\.(\d+))?)?$/,
|
---|
| 149 | (asyncFlag, major, minor) => {
|
---|
| 150 | const v = versionDependent(major, minor);
|
---|
| 151 | // see https://node.green/
|
---|
| 152 | return {
|
---|
| 153 | node: true,
|
---|
| 154 | electron: false,
|
---|
| 155 | nwjs: false,
|
---|
| 156 | web: false,
|
---|
| 157 | webworker: false,
|
---|
| 158 | browser: false,
|
---|
| 159 |
|
---|
| 160 | require: !asyncFlag,
|
---|
| 161 | nodeBuiltins: true,
|
---|
| 162 | global: true,
|
---|
| 163 | document: false,
|
---|
| 164 | fetchWasm: false,
|
---|
| 165 | importScripts: false,
|
---|
| 166 | importScriptsInWorker: false,
|
---|
| 167 |
|
---|
| 168 | globalThis: v(12),
|
---|
| 169 | const: v(6),
|
---|
| 170 | arrowFunction: v(6),
|
---|
| 171 | forOf: v(5),
|
---|
| 172 | destructuring: v(6),
|
---|
| 173 | bigIntLiteral: v(10, 4),
|
---|
| 174 | dynamicImport: v(12, 17),
|
---|
| 175 | dynamicImportInWorker: major ? false : undefined,
|
---|
| 176 | module: v(12, 17)
|
---|
| 177 | };
|
---|
| 178 | }
|
---|
| 179 | ],
|
---|
| 180 | [
|
---|
| 181 | "electron[X[.Y]]-main/preload/renderer",
|
---|
| 182 | "Electron in version X.Y. Script is running in main, preload resp. renderer context.",
|
---|
| 183 | /^electron(\d+(?:\.(\d+))?)?-(main|preload|renderer)$/,
|
---|
| 184 | (major, minor, context) => {
|
---|
| 185 | const v = versionDependent(major, minor);
|
---|
| 186 | // see https://node.green/ + https://github.com/electron/releases
|
---|
| 187 | return {
|
---|
| 188 | node: true,
|
---|
| 189 | electron: true,
|
---|
| 190 | web: context !== "main",
|
---|
| 191 | webworker: false,
|
---|
| 192 | browser: false,
|
---|
| 193 | nwjs: false,
|
---|
| 194 |
|
---|
| 195 | electronMain: context === "main",
|
---|
| 196 | electronPreload: context === "preload",
|
---|
| 197 | electronRenderer: context === "renderer",
|
---|
| 198 |
|
---|
| 199 | global: true,
|
---|
| 200 | nodeBuiltins: true,
|
---|
| 201 | require: true,
|
---|
| 202 | document: context === "renderer",
|
---|
| 203 | fetchWasm: context === "renderer",
|
---|
| 204 | importScripts: false,
|
---|
| 205 | importScriptsInWorker: true,
|
---|
| 206 |
|
---|
| 207 | globalThis: v(5),
|
---|
| 208 | const: v(1, 1),
|
---|
| 209 | arrowFunction: v(1, 1),
|
---|
| 210 | forOf: v(0, 36),
|
---|
| 211 | destructuring: v(1, 1),
|
---|
| 212 | bigIntLiteral: v(4),
|
---|
| 213 | dynamicImport: v(11),
|
---|
| 214 | dynamicImportInWorker: major ? false : undefined,
|
---|
| 215 | module: v(11)
|
---|
| 216 | };
|
---|
| 217 | }
|
---|
| 218 | ],
|
---|
| 219 | [
|
---|
| 220 | "nwjs[X[.Y]] / node-webkit[X[.Y]]",
|
---|
| 221 | "NW.js in version X.Y.",
|
---|
| 222 | /^(?:nwjs|node-webkit)(\d+(?:\.(\d+))?)?$/,
|
---|
| 223 | (major, minor) => {
|
---|
| 224 | const v = versionDependent(major, minor);
|
---|
| 225 | // see https://node.green/ + https://github.com/nwjs/nw.js/blob/nw48/CHANGELOG.md
|
---|
| 226 | return {
|
---|
| 227 | node: true,
|
---|
| 228 | web: true,
|
---|
| 229 | nwjs: true,
|
---|
| 230 | webworker: null,
|
---|
| 231 | browser: false,
|
---|
| 232 | electron: false,
|
---|
| 233 |
|
---|
| 234 | global: true,
|
---|
| 235 | nodeBuiltins: true,
|
---|
| 236 | document: false,
|
---|
| 237 | importScriptsInWorker: false,
|
---|
| 238 | fetchWasm: false,
|
---|
| 239 | importScripts: false,
|
---|
| 240 | require: false,
|
---|
| 241 |
|
---|
| 242 | globalThis: v(0, 43),
|
---|
| 243 | const: v(0, 15),
|
---|
| 244 | arrowFunction: v(0, 15),
|
---|
| 245 | forOf: v(0, 13),
|
---|
| 246 | destructuring: v(0, 15),
|
---|
| 247 | bigIntLiteral: v(0, 32),
|
---|
| 248 | dynamicImport: v(0, 43),
|
---|
| 249 | dynamicImportInWorker: major ? false : undefined,
|
---|
| 250 | module: v(0, 43)
|
---|
| 251 | };
|
---|
| 252 | }
|
---|
| 253 | ],
|
---|
| 254 | [
|
---|
| 255 | "esX",
|
---|
| 256 | "EcmaScript in this version. Examples: es2020, es5.",
|
---|
| 257 | /^es(\d+)$/,
|
---|
| 258 | version => {
|
---|
| 259 | let v = +version;
|
---|
| 260 | if (v < 1000) v = v + 2009;
|
---|
| 261 | return {
|
---|
| 262 | const: v >= 2015,
|
---|
| 263 | arrowFunction: v >= 2015,
|
---|
| 264 | forOf: v >= 2015,
|
---|
| 265 | destructuring: v >= 2015,
|
---|
| 266 | module: v >= 2015,
|
---|
| 267 | globalThis: v >= 2020,
|
---|
| 268 | bigIntLiteral: v >= 2020,
|
---|
| 269 | dynamicImport: v >= 2020,
|
---|
| 270 | dynamicImportInWorker: v >= 2020
|
---|
| 271 | };
|
---|
| 272 | }
|
---|
| 273 | ]
|
---|
| 274 | ];
|
---|
| 275 |
|
---|
| 276 | /**
|
---|
| 277 | * @param {string} target the target
|
---|
| 278 | * @param {string} context the context directory
|
---|
| 279 | * @returns {TargetProperties} target properties
|
---|
| 280 | */
|
---|
| 281 | const getTargetProperties = (target, context) => {
|
---|
| 282 | for (const [, , regExp, handler] of TARGETS) {
|
---|
| 283 | const match = regExp.exec(target);
|
---|
| 284 | if (match) {
|
---|
| 285 | const [, ...args] = match;
|
---|
| 286 | const result = handler(...args, context);
|
---|
| 287 | if (result) return result;
|
---|
| 288 | }
|
---|
| 289 | }
|
---|
| 290 | throw new Error(
|
---|
| 291 | `Unknown target '${target}'. The following targets are supported:\n${TARGETS.map(
|
---|
| 292 | ([name, description]) => `* ${name}: ${description}`
|
---|
| 293 | ).join("\n")}`
|
---|
| 294 | );
|
---|
| 295 | };
|
---|
| 296 |
|
---|
| 297 | const mergeTargetProperties = targetProperties => {
|
---|
| 298 | const keys = new Set();
|
---|
| 299 | for (const tp of targetProperties) {
|
---|
| 300 | for (const key of Object.keys(tp)) {
|
---|
| 301 | keys.add(key);
|
---|
| 302 | }
|
---|
| 303 | }
|
---|
| 304 | const result = {};
|
---|
| 305 | for (const key of keys) {
|
---|
| 306 | let hasTrue = false;
|
---|
| 307 | let hasFalse = false;
|
---|
| 308 | for (const tp of targetProperties) {
|
---|
| 309 | const value = tp[key];
|
---|
| 310 | switch (value) {
|
---|
| 311 | case true:
|
---|
| 312 | hasTrue = true;
|
---|
| 313 | break;
|
---|
| 314 | case false:
|
---|
| 315 | hasFalse = true;
|
---|
| 316 | break;
|
---|
| 317 | }
|
---|
| 318 | }
|
---|
| 319 | if (hasTrue || hasFalse)
|
---|
| 320 | result[key] = hasFalse && hasTrue ? null : hasTrue ? true : false;
|
---|
| 321 | }
|
---|
| 322 | return /** @type {TargetProperties} */ (result);
|
---|
| 323 | };
|
---|
| 324 |
|
---|
| 325 | /**
|
---|
| 326 | * @param {string[]} targets the targets
|
---|
| 327 | * @param {string} context the context directory
|
---|
| 328 | * @returns {TargetProperties} target properties
|
---|
| 329 | */
|
---|
| 330 | const getTargetsProperties = (targets, context) => {
|
---|
| 331 | return mergeTargetProperties(
|
---|
| 332 | targets.map(t => getTargetProperties(t, context))
|
---|
| 333 | );
|
---|
| 334 | };
|
---|
| 335 |
|
---|
| 336 | exports.getDefaultTarget = getDefaultTarget;
|
---|
| 337 | exports.getTargetProperties = getTargetProperties;
|
---|
| 338 | exports.getTargetsProperties = getTargetsProperties;
|
---|