@jsenv/core 41.5.3 → 41.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build/build.js +77 -16
- package/dist/start_dev_server/start_dev_server.js +41 -8
- package/package.json +1 -1
- package/src/build/build.js +36 -7
- package/src/plugins/import_meta_scenarios/jsenv_plugin_import_meta_scenarios.js +13 -6
- package/src/plugins/plugins.js +5 -1
- package/src/plugins/resolution_node_esm/jsenv_plugin_node_esm_resolution.js +5 -0
- package/src/plugins/resolution_node_esm/node_esm_resolver.js +18 -1
package/dist/build/build.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { parseHtml, injectHtmlNodeAsEarlyAsPossible, createHtmlNode, stringifyHtmlAst, applyBabelPlugins, generateUrlForInlineContent, injectJsenvScript, parseJsWithAcorn, visitHtmlNodes, analyzeScriptNode, getHtmlNodeText, getHtmlNodeAttribute, getHtmlNodePosition, getUrlForContentInsideHtml, setHtmlNodeAttributes, setHtmlNodeText, parseCssUrls, getHtmlNodeAttributePosition, parseSrcSet, removeHtmlNodeText, parseJsUrls, getUrlForContentInsideJs, renderCssTemplateLiteral, visitJsAst, getImportMetaPropertyName, visitJsAstUntil, analyzeLinkNode, findHtmlNode, removeHtmlNode, insertHtmlNodeAfter } from "@jsenv/ast";
|
|
2
|
+
import { lookupPackageDirectory, urlIsOrIsInsideOf, registerDirectoryLifecycle, urlToRelativeUrl, createDetailedMessage, stringifyUrlSite, generateContentFrame, validateResponseIntegrity, ensureWindowsDriveLetter, setUrlFilename, moveUrl, getCallerPosition, urlToBasename, urlToExtension, asSpecifierWithoutSearch, asUrlWithoutSearch, injectQueryParamsIntoSpecifier, bufferToEtag, isFileSystemPath, urlToPathname, setUrlBasename, urlToFileSystemPath, writeFileSync, createLogger, URL_META, applyNodeEsmResolution, normalizeUrl, ANSI, RUNTIME_COMPAT, CONTENT_TYPE, readPackageAtOrNull, urlToFilename, DATA_URL, errorToHTML, normalizeImportMap, composeTwoImportMaps, resolveImport, JS_QUOTES, readCustomConditionsFromProcessArgs, collectFiles, readEntryStatSync, applyFileSystemMagicResolution, getExtensionsToTry, ensurePathnameTrailingSlash, compareFileUrls, setUrlExtension, isSpecifierForNodeBuiltin, injectQueryParams, renderDetails, humanizeDuration, humanizeFileSize, renderTable, renderBigSection, distributePercentages, humanizeMemory, comparePathnames, UNICODE, escapeRegexpSpecialChars, injectQueryParamIntoSpecifierWithoutEncoding, renderUrlOrRelativeUrlFilename, assertAndNormalizeDirectoryUrl, Abort, raceProcessTeardownEvents, startMonitoringMemoryUsage, inferRuntimeCompatFromClosestPackage, browserDefaultRuntimeCompat, nodeDefaultRuntimeCompat, clearDirectorySync, createTaskLog, createLookupPackageDirectory, ensureEmptyDirectory, updateJsonFileSync, createDynamicLog } from "./jsenv_core_packages.js";
|
|
2
3
|
import { bundleJsModules, jsenvPluginBundling } from "@jsenv/plugin-bundling";
|
|
3
4
|
import { jsenvPluginMinification } from "@jsenv/plugin-minification";
|
|
4
5
|
import { jsenvPluginTranspilation, jsenvPluginJsModuleFallback } from "@jsenv/plugin-transpilation";
|
|
5
6
|
import { memoryUsage } from "node:process";
|
|
6
7
|
import { readFileSync, existsSync, realpathSync, readdirSync, lstatSync, statSync } from "node:fs";
|
|
7
|
-
import { lookupPackageDirectory, urlIsOrIsInsideOf, registerDirectoryLifecycle, urlToRelativeUrl, createDetailedMessage, stringifyUrlSite, generateContentFrame, validateResponseIntegrity, ensureWindowsDriveLetter, setUrlFilename, moveUrl, getCallerPosition, urlToBasename, urlToExtension, asSpecifierWithoutSearch, asUrlWithoutSearch, injectQueryParamsIntoSpecifier, bufferToEtag, isFileSystemPath, urlToPathname, setUrlBasename, urlToFileSystemPath, writeFileSync, createLogger, URL_META, applyNodeEsmResolution, normalizeUrl, ANSI, RUNTIME_COMPAT, CONTENT_TYPE, readPackageAtOrNull, urlToFilename, DATA_URL, errorToHTML, normalizeImportMap, composeTwoImportMaps, resolveImport, JS_QUOTES, readCustomConditionsFromProcessArgs, collectFiles, readEntryStatSync, applyFileSystemMagicResolution, getExtensionsToTry, ensurePathnameTrailingSlash, compareFileUrls, setUrlExtension, isSpecifierForNodeBuiltin, injectQueryParams, renderDetails, humanizeDuration, humanizeFileSize, renderTable, renderBigSection, distributePercentages, humanizeMemory, comparePathnames, UNICODE, escapeRegexpSpecialChars, injectQueryParamIntoSpecifierWithoutEncoding, renderUrlOrRelativeUrlFilename, assertAndNormalizeDirectoryUrl, Abort, raceProcessTeardownEvents, startMonitoringMemoryUsage, inferRuntimeCompatFromClosestPackage, browserDefaultRuntimeCompat, nodeDefaultRuntimeCompat, clearDirectorySync, createTaskLog, createLookupPackageDirectory, ensureEmptyDirectory, updateJsonFileSync, createDynamicLog } from "./jsenv_core_packages.js";
|
|
8
8
|
import { pathToFileURL } from "node:url";
|
|
9
9
|
import { generateSourcemapFileUrl, createMagicSource, composeTwoSourcemaps, generateSourcemapDataUrl, SOURCEMAP } from "@jsenv/sourcemap";
|
|
10
10
|
import { createPluginsController } from "@jsenv/server/src/plugins_controller.js";
|
|
@@ -6023,6 +6023,8 @@ const createNodeEsmResolver = ({
|
|
|
6023
6023
|
packageDirectory,
|
|
6024
6024
|
runtimeCompat,
|
|
6025
6025
|
rootDirectoryUrl,
|
|
6026
|
+
dev,
|
|
6027
|
+
build,
|
|
6026
6028
|
packageConditions = {},
|
|
6027
6029
|
packageConditionsConfig,
|
|
6028
6030
|
preservesSymlink,
|
|
@@ -6040,6 +6042,8 @@ const createNodeEsmResolver = ({
|
|
|
6040
6042
|
packageConditionsConfig,
|
|
6041
6043
|
rootDirectoryUrl,
|
|
6042
6044
|
runtimeCompat,
|
|
6045
|
+
dev,
|
|
6046
|
+
build,
|
|
6043
6047
|
preservesSymlink,
|
|
6044
6048
|
},
|
|
6045
6049
|
);
|
|
@@ -6201,6 +6205,8 @@ const createBuildPackageConditions = (
|
|
|
6201
6205
|
packageConditionsConfig,
|
|
6202
6206
|
rootDirectoryUrl,
|
|
6203
6207
|
runtimeCompat,
|
|
6208
|
+
dev,
|
|
6209
|
+
build,
|
|
6204
6210
|
preservesSymlink,
|
|
6205
6211
|
},
|
|
6206
6212
|
) => {
|
|
@@ -6290,8 +6296,19 @@ const createBuildPackageConditions = (
|
|
|
6290
6296
|
};
|
|
6291
6297
|
|
|
6292
6298
|
const conditionDefaultResolvers = {
|
|
6299
|
+
// "dev:*" conditions target packages under active development: they
|
|
6300
|
+
// apply to files resolved outside node_modules (sources, workspace
|
|
6301
|
+
// symlinks), never to installed packages.
|
|
6293
6302
|
"dev:*": devResolver,
|
|
6294
|
-
"development"
|
|
6303
|
+
// "development"/"production" follow the ecosystem semantics
|
|
6304
|
+
// (Vite, webpack): they describe how the app runs, not where the
|
|
6305
|
+
// package lives, so they apply to every package, node_modules
|
|
6306
|
+
// included. Dev server resolves "development"; build resolves
|
|
6307
|
+
// "production", unless the build is dev-flavored
|
|
6308
|
+
// (build({ entryPoints: { "./file.js": { dev: true } } }))
|
|
6309
|
+
// in which case it resolves "development" too.
|
|
6310
|
+
"development": Boolean(dev),
|
|
6311
|
+
"production": Boolean(build) && !dev,
|
|
6295
6312
|
"node": nodeRuntimeEnabled,
|
|
6296
6313
|
"browser": !nodeRuntimeEnabled,
|
|
6297
6314
|
"import": true,
|
|
@@ -6505,6 +6522,7 @@ const jsenvPluginNodeEsmResolution = ({
|
|
|
6505
6522
|
resolutionConfig = {},
|
|
6506
6523
|
packageConditions,
|
|
6507
6524
|
packageConditionsConfig = {},
|
|
6525
|
+
dev,
|
|
6508
6526
|
}) => {
|
|
6509
6527
|
let nodeEsmResolverDefault;
|
|
6510
6528
|
const resolverMap = new Map();
|
|
@@ -6526,6 +6544,8 @@ const jsenvPluginNodeEsmResolution = ({
|
|
|
6526
6544
|
packageDirectory,
|
|
6527
6545
|
runtimeCompat: kitchenContext.runtimeCompat,
|
|
6528
6546
|
rootDirectoryUrl: kitchenContext.rootDirectoryUrl,
|
|
6547
|
+
dev: dev === undefined ? kitchenContext.dev : dev,
|
|
6548
|
+
build: kitchenContext.build,
|
|
6529
6549
|
packageConditions,
|
|
6530
6550
|
packageConditionsConfig: {
|
|
6531
6551
|
...kitchenContext.packageConditionsConfig,
|
|
@@ -6543,6 +6563,8 @@ const jsenvPluginNodeEsmResolution = ({
|
|
|
6543
6563
|
packageDirectory,
|
|
6544
6564
|
runtimeCompat: kitchenContext.runtimeCompat,
|
|
6545
6565
|
rootDirectoryUrl: kitchenContext.rootDirectoryUrl,
|
|
6566
|
+
dev: dev === undefined ? kitchenContext.dev : dev,
|
|
6567
|
+
build: kitchenContext.build,
|
|
6546
6568
|
// preservesSymlink: true,
|
|
6547
6569
|
packageConditions,
|
|
6548
6570
|
packageConditionsConfig: {
|
|
@@ -8189,11 +8211,16 @@ const babelPluginMetadataExpressionPaths = (
|
|
|
8189
8211
|
* - left as is to be evaluated to undefined (import.meta.build but it's the dev server)
|
|
8190
8212
|
* - replaced by undefined (import.meta.dev but it's build; the goal is to ensure it's tree-shaked)
|
|
8191
8213
|
*
|
|
8214
|
+
* A build can also be dev-flavored (build({ entryPoints: { "./file.js": { dev: true } } })):
|
|
8215
|
+
* import.meta.dev is then replaced by true, so a package can publish a build
|
|
8216
|
+
* that keeps its dev-only code, exposed to consumers via the "development"
|
|
8217
|
+
* package export condition.
|
|
8218
|
+
*
|
|
8192
8219
|
* TODO: ideally during dev we would keep import.meta.dev and ensure we set it to true rather than replacing it with true?
|
|
8193
8220
|
*/
|
|
8194
8221
|
|
|
8195
8222
|
|
|
8196
|
-
const jsenvPluginImportMetaScenarios = () => {
|
|
8223
|
+
const jsenvPluginImportMetaScenarios = ({ dev } = {}) => {
|
|
8197
8224
|
return {
|
|
8198
8225
|
name: "jsenv:import_meta_scenario",
|
|
8199
8226
|
appliesDuring: "*",
|
|
@@ -8223,17 +8250,19 @@ const jsenvPluginImportMetaScenarios = () => {
|
|
|
8223
8250
|
}
|
|
8224
8251
|
},
|
|
8225
8252
|
});
|
|
8226
|
-
const
|
|
8253
|
+
const devNodes = importMetaScenarioNodes.dev;
|
|
8254
|
+
const buildNodes = importMetaScenarioNodes.build;
|
|
8227
8255
|
const replacements = [];
|
|
8228
8256
|
const replace = (node, value) => {
|
|
8229
8257
|
replacements.push({ node, value });
|
|
8230
8258
|
};
|
|
8231
8259
|
if (urlInfo.context.build) {
|
|
8232
8260
|
// during build ensure replacement for tree-shaking
|
|
8233
|
-
|
|
8234
|
-
|
|
8261
|
+
// (or for keeping dev code when the build is dev-flavored)
|
|
8262
|
+
devNodes.forEach((node) => {
|
|
8263
|
+
replace(node, dev ? "true" : "undefined");
|
|
8235
8264
|
});
|
|
8236
|
-
|
|
8265
|
+
buildNodes.forEach((node) => {
|
|
8237
8266
|
replace(node, "true");
|
|
8238
8267
|
});
|
|
8239
8268
|
} else {
|
|
@@ -8241,7 +8270,7 @@ const jsenvPluginImportMetaScenarios = () => {
|
|
|
8241
8270
|
// it will be evaluated to undefined.
|
|
8242
8271
|
// Moreover it can be surprising to see some "undefined"
|
|
8243
8272
|
// when source file contains "import.meta.build"
|
|
8244
|
-
|
|
8273
|
+
devNodes.forEach((node) => {
|
|
8245
8274
|
replace(node, "true");
|
|
8246
8275
|
});
|
|
8247
8276
|
}
|
|
@@ -10119,6 +10148,9 @@ const getCorePlugins = ({
|
|
|
10119
10148
|
runtimeCompat,
|
|
10120
10149
|
packageDirectory,
|
|
10121
10150
|
sourceFilesConfig,
|
|
10151
|
+
// during build, a dev-flavored entry point sets dev: true
|
|
10152
|
+
// (during dev the kitchen context already says so, no need to pass it)
|
|
10153
|
+
dev,
|
|
10122
10154
|
|
|
10123
10155
|
referenceAnalysis = {},
|
|
10124
10156
|
nodeEsmResolution = {},
|
|
@@ -10215,6 +10247,7 @@ const getCorePlugins = ({
|
|
|
10215
10247
|
resolutionConfig: nodeEsmResolution,
|
|
10216
10248
|
packageConditions,
|
|
10217
10249
|
packageConditionsConfig,
|
|
10250
|
+
dev,
|
|
10218
10251
|
}),
|
|
10219
10252
|
]
|
|
10220
10253
|
: []),
|
|
@@ -10232,7 +10265,7 @@ const getCorePlugins = ({
|
|
|
10232
10265
|
|
|
10233
10266
|
jsenvPluginImportMetaCss(),
|
|
10234
10267
|
jsenvPluginCommonJsGlobals(),
|
|
10235
|
-
jsenvPluginImportMetaScenarios(),
|
|
10268
|
+
jsenvPluginImportMetaScenarios({ dev }),
|
|
10236
10269
|
...(scenarioPlaceholders ? [jsenvPluginGlobalScenarios()] : []),
|
|
10237
10270
|
jsenvPluginNodeRuntime({ runtimeCompat }),
|
|
10238
10271
|
|
|
@@ -12413,6 +12446,12 @@ const jsenvPluginMappings = (mappings) => {
|
|
|
12413
12446
|
* Relative URL where this entry point will be written in the build directory
|
|
12414
12447
|
* @param {object} [entryPoint.runtimeCompat]
|
|
12415
12448
|
* Runtime compatibility configuration for this entry point
|
|
12449
|
+
* @param {boolean} [entryPoint.dev=false]
|
|
12450
|
+
* Produce a dev-flavored build: import.meta.dev is replaced by true
|
|
12451
|
+
* (instead of undefined) and dependencies resolve their "development"
|
|
12452
|
+
* package export condition (instead of "production"). Meant for packages
|
|
12453
|
+
* publishing two flavors, the dev one exposed to consumers via the
|
|
12454
|
+
* "development" condition in their package.json "exports"
|
|
12416
12455
|
* @param {string} [entryPoint.assetsDirectory]
|
|
12417
12456
|
* Directory where asset files will be written for this entry point
|
|
12418
12457
|
* @param {string|url} [entryPoint.base]
|
|
@@ -12536,12 +12575,10 @@ entryPoints: {
|
|
|
12536
12575
|
let runtimeType;
|
|
12537
12576
|
{
|
|
12538
12577
|
if (isBareSpecifier(key)) {
|
|
12539
|
-
|
|
12540
|
-
|
|
12541
|
-
|
|
12542
|
-
|
|
12543
|
-
"import",
|
|
12544
|
-
];
|
|
12578
|
+
// "development" is not resolved here on purpose: it points at a
|
|
12579
|
+
// package's dev-flavored dist, which is not a build input;
|
|
12580
|
+
// "dev:*" points at sources, which are.
|
|
12581
|
+
const packageConditions = ["dev:*", "node", "import"];
|
|
12545
12582
|
try {
|
|
12546
12583
|
const { url, type } = applyNodeEsmResolution({
|
|
12547
12584
|
conditions: packageConditions,
|
|
@@ -13148,10 +13185,31 @@ entryPoints: {
|
|
|
13148
13185
|
).href;
|
|
13149
13186
|
packageSideEffectUrlSet.add(sideEffectFileUrl);
|
|
13150
13187
|
}
|
|
13188
|
+
// A side effect entry is outdated (and pruned) only when this build
|
|
13189
|
+
// owns it: it is inside the build directory AND the clean patterns
|
|
13190
|
+
// would remove it. A file kept by buildDirectoryCleanPatterns
|
|
13191
|
+
// belongs to another build writing inside this directory (e.g. a
|
|
13192
|
+
// dev-flavored build in a subdirectory, possibly running in
|
|
13193
|
+
// parallel): its entries must be preserved, like files outside the
|
|
13194
|
+
// build directory.
|
|
13195
|
+
const cleanAssociations = URL_META.resolveAssociations(
|
|
13196
|
+
{ remove: buildDirectoryCleanPatterns },
|
|
13197
|
+
buildDirectoryUrl,
|
|
13198
|
+
);
|
|
13199
|
+
const isOwnedByThisBuild = (url) => {
|
|
13200
|
+
if (!urlIsOrIsInsideOf(url, buildDirectoryUrl)) {
|
|
13201
|
+
return false;
|
|
13202
|
+
}
|
|
13203
|
+
const { remove } = URL_META.applyAssociations({
|
|
13204
|
+
url,
|
|
13205
|
+
associations: cleanAssociations,
|
|
13206
|
+
});
|
|
13207
|
+
return Boolean(remove);
|
|
13208
|
+
};
|
|
13151
13209
|
let hasSomeOutdatedSideEffectUrl = false;
|
|
13152
13210
|
for (const packageSideEffectUrl of packageSideEffectUrlSet) {
|
|
13153
13211
|
if (
|
|
13154
|
-
|
|
13212
|
+
isOwnedByThisBuild(packageSideEffectUrl) &&
|
|
13155
13213
|
!buildSideEffectUrlSet.has(packageSideEffectUrl)
|
|
13156
13214
|
) {
|
|
13157
13215
|
hasSomeOutdatedSideEffectUrl = true;
|
|
@@ -13273,6 +13331,7 @@ entryPoints: {
|
|
|
13273
13331
|
const entryPointDefaultParams = {
|
|
13274
13332
|
buildRelativeUrl: undefined,
|
|
13275
13333
|
mode: undefined,
|
|
13334
|
+
dev: false,
|
|
13276
13335
|
runtimeCompat: undefined,
|
|
13277
13336
|
plugins: [],
|
|
13278
13337
|
mappings: undefined,
|
|
@@ -13328,6 +13387,7 @@ const prepareEntryPointBuild = async (
|
|
|
13328
13387
|
let {
|
|
13329
13388
|
buildRelativeUrl,
|
|
13330
13389
|
mode,
|
|
13390
|
+
dev,
|
|
13331
13391
|
runtimeCompat,
|
|
13332
13392
|
plugins,
|
|
13333
13393
|
mappings,
|
|
@@ -13508,6 +13568,7 @@ const prepareEntryPointBuild = async (
|
|
|
13508
13568
|
packageDirectory,
|
|
13509
13569
|
rootDirectoryUrl: sourceDirectoryUrl,
|
|
13510
13570
|
runtimeCompat,
|
|
13571
|
+
dev,
|
|
13511
13572
|
referenceAnalysis,
|
|
13512
13573
|
nodeEsmResolution,
|
|
13513
13574
|
packageConditions,
|
|
@@ -2689,6 +2689,8 @@ const createNodeEsmResolver = ({
|
|
|
2689
2689
|
packageDirectory,
|
|
2690
2690
|
runtimeCompat,
|
|
2691
2691
|
rootDirectoryUrl,
|
|
2692
|
+
dev,
|
|
2693
|
+
build,
|
|
2692
2694
|
packageConditions = {},
|
|
2693
2695
|
packageConditionsConfig,
|
|
2694
2696
|
preservesSymlink,
|
|
@@ -2706,6 +2708,8 @@ const createNodeEsmResolver = ({
|
|
|
2706
2708
|
packageConditionsConfig,
|
|
2707
2709
|
rootDirectoryUrl,
|
|
2708
2710
|
runtimeCompat,
|
|
2711
|
+
dev,
|
|
2712
|
+
build,
|
|
2709
2713
|
preservesSymlink,
|
|
2710
2714
|
},
|
|
2711
2715
|
);
|
|
@@ -2867,6 +2871,8 @@ const createBuildPackageConditions = (
|
|
|
2867
2871
|
packageConditionsConfig,
|
|
2868
2872
|
rootDirectoryUrl,
|
|
2869
2873
|
runtimeCompat,
|
|
2874
|
+
dev,
|
|
2875
|
+
build,
|
|
2870
2876
|
preservesSymlink,
|
|
2871
2877
|
},
|
|
2872
2878
|
) => {
|
|
@@ -2956,8 +2962,19 @@ const createBuildPackageConditions = (
|
|
|
2956
2962
|
};
|
|
2957
2963
|
|
|
2958
2964
|
const conditionDefaultResolvers = {
|
|
2965
|
+
// "dev:*" conditions target packages under active development: they
|
|
2966
|
+
// apply to files resolved outside node_modules (sources, workspace
|
|
2967
|
+
// symlinks), never to installed packages.
|
|
2959
2968
|
"dev:*": devResolver,
|
|
2960
|
-
"development"
|
|
2969
|
+
// "development"/"production" follow the ecosystem semantics
|
|
2970
|
+
// (Vite, webpack): they describe how the app runs, not where the
|
|
2971
|
+
// package lives, so they apply to every package, node_modules
|
|
2972
|
+
// included. Dev server resolves "development"; build resolves
|
|
2973
|
+
// "production", unless the build is dev-flavored
|
|
2974
|
+
// (build({ entryPoints: { "./file.js": { dev: true } } }))
|
|
2975
|
+
// in which case it resolves "development" too.
|
|
2976
|
+
"development": Boolean(dev),
|
|
2977
|
+
"production": Boolean(build) && !dev,
|
|
2961
2978
|
"node": nodeRuntimeEnabled,
|
|
2962
2979
|
"browser": !nodeRuntimeEnabled,
|
|
2963
2980
|
"import": true,
|
|
@@ -3171,6 +3188,7 @@ const jsenvPluginNodeEsmResolution = ({
|
|
|
3171
3188
|
resolutionConfig = {},
|
|
3172
3189
|
packageConditions,
|
|
3173
3190
|
packageConditionsConfig = {},
|
|
3191
|
+
dev,
|
|
3174
3192
|
}) => {
|
|
3175
3193
|
let nodeEsmResolverDefault;
|
|
3176
3194
|
const resolverMap = new Map();
|
|
@@ -3192,6 +3210,8 @@ const jsenvPluginNodeEsmResolution = ({
|
|
|
3192
3210
|
packageDirectory,
|
|
3193
3211
|
runtimeCompat: kitchenContext.runtimeCompat,
|
|
3194
3212
|
rootDirectoryUrl: kitchenContext.rootDirectoryUrl,
|
|
3213
|
+
dev: dev === undefined ? kitchenContext.dev : dev,
|
|
3214
|
+
build: kitchenContext.build,
|
|
3195
3215
|
packageConditions,
|
|
3196
3216
|
packageConditionsConfig: {
|
|
3197
3217
|
...kitchenContext.packageConditionsConfig,
|
|
@@ -3209,6 +3229,8 @@ const jsenvPluginNodeEsmResolution = ({
|
|
|
3209
3229
|
packageDirectory,
|
|
3210
3230
|
runtimeCompat: kitchenContext.runtimeCompat,
|
|
3211
3231
|
rootDirectoryUrl: kitchenContext.rootDirectoryUrl,
|
|
3232
|
+
dev: dev === undefined ? kitchenContext.dev : dev,
|
|
3233
|
+
build: kitchenContext.build,
|
|
3212
3234
|
// preservesSymlink: true,
|
|
3213
3235
|
packageConditions,
|
|
3214
3236
|
packageConditionsConfig: {
|
|
@@ -5994,11 +6016,16 @@ const babelPluginMetadataExpressionPaths = (
|
|
|
5994
6016
|
* - left as is to be evaluated to undefined (import.meta.build but it's the dev server)
|
|
5995
6017
|
* - replaced by undefined (import.meta.dev but it's build; the goal is to ensure it's tree-shaked)
|
|
5996
6018
|
*
|
|
6019
|
+
* A build can also be dev-flavored (build({ entryPoints: { "./file.js": { dev: true } } })):
|
|
6020
|
+
* import.meta.dev is then replaced by true, so a package can publish a build
|
|
6021
|
+
* that keeps its dev-only code, exposed to consumers via the "development"
|
|
6022
|
+
* package export condition.
|
|
6023
|
+
*
|
|
5997
6024
|
* TODO: ideally during dev we would keep import.meta.dev and ensure we set it to true rather than replacing it with true?
|
|
5998
6025
|
*/
|
|
5999
6026
|
|
|
6000
6027
|
|
|
6001
|
-
const jsenvPluginImportMetaScenarios = () => {
|
|
6028
|
+
const jsenvPluginImportMetaScenarios = ({ dev } = {}) => {
|
|
6002
6029
|
return {
|
|
6003
6030
|
name: "jsenv:import_meta_scenario",
|
|
6004
6031
|
appliesDuring: "*",
|
|
@@ -6028,17 +6055,19 @@ const jsenvPluginImportMetaScenarios = () => {
|
|
|
6028
6055
|
}
|
|
6029
6056
|
},
|
|
6030
6057
|
});
|
|
6031
|
-
const
|
|
6058
|
+
const devNodes = importMetaScenarioNodes.dev;
|
|
6059
|
+
const buildNodes = importMetaScenarioNodes.build;
|
|
6032
6060
|
const replacements = [];
|
|
6033
6061
|
const replace = (node, value) => {
|
|
6034
6062
|
replacements.push({ node, value });
|
|
6035
6063
|
};
|
|
6036
6064
|
if (urlInfo.context.build) {
|
|
6037
6065
|
// during build ensure replacement for tree-shaking
|
|
6038
|
-
|
|
6039
|
-
|
|
6066
|
+
// (or for keeping dev code when the build is dev-flavored)
|
|
6067
|
+
devNodes.forEach((node) => {
|
|
6068
|
+
replace(node, dev ? "true" : "undefined");
|
|
6040
6069
|
});
|
|
6041
|
-
|
|
6070
|
+
buildNodes.forEach((node) => {
|
|
6042
6071
|
replace(node, "true");
|
|
6043
6072
|
});
|
|
6044
6073
|
} else {
|
|
@@ -6046,7 +6075,7 @@ const jsenvPluginImportMetaScenarios = () => {
|
|
|
6046
6075
|
// it will be evaluated to undefined.
|
|
6047
6076
|
// Moreover it can be surprising to see some "undefined"
|
|
6048
6077
|
// when source file contains "import.meta.build"
|
|
6049
|
-
|
|
6078
|
+
devNodes.forEach((node) => {
|
|
6050
6079
|
replace(node, "true");
|
|
6051
6080
|
});
|
|
6052
6081
|
}
|
|
@@ -7984,6 +8013,9 @@ const getCorePlugins = ({
|
|
|
7984
8013
|
runtimeCompat,
|
|
7985
8014
|
packageDirectory,
|
|
7986
8015
|
sourceFilesConfig,
|
|
8016
|
+
// during build, a dev-flavored entry point sets dev: true
|
|
8017
|
+
// (during dev the kitchen context already says so, no need to pass it)
|
|
8018
|
+
dev,
|
|
7987
8019
|
|
|
7988
8020
|
referenceAnalysis = {},
|
|
7989
8021
|
nodeEsmResolution = {},
|
|
@@ -8080,6 +8112,7 @@ const getCorePlugins = ({
|
|
|
8080
8112
|
resolutionConfig: nodeEsmResolution,
|
|
8081
8113
|
packageConditions,
|
|
8082
8114
|
packageConditionsConfig,
|
|
8115
|
+
dev,
|
|
8083
8116
|
}),
|
|
8084
8117
|
]
|
|
8085
8118
|
: []),
|
|
@@ -8097,7 +8130,7 @@ const getCorePlugins = ({
|
|
|
8097
8130
|
|
|
8098
8131
|
jsenvPluginImportMetaCss(),
|
|
8099
8132
|
jsenvPluginCommonJsGlobals(),
|
|
8100
|
-
jsenvPluginImportMetaScenarios(),
|
|
8133
|
+
jsenvPluginImportMetaScenarios({ dev }),
|
|
8101
8134
|
...(scenarioPlaceholders ? [jsenvPluginGlobalScenarios()] : []),
|
|
8102
8135
|
jsenvPluginNodeRuntime({ runtimeCompat }),
|
|
8103
8136
|
|
package/package.json
CHANGED
package/src/build/build.js
CHANGED
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
} from "@jsenv/humanize";
|
|
40
40
|
import { applyNodeEsmResolution } from "@jsenv/node-esm-resolution";
|
|
41
41
|
import { startMonitoringMemoryUsage } from "@jsenv/os-metrics";
|
|
42
|
+
import { URL_META } from "@jsenv/url-meta";
|
|
42
43
|
import { jsenvPluginBundling } from "@jsenv/plugin-bundling";
|
|
43
44
|
import { jsenvPluginMinification } from "@jsenv/plugin-minification";
|
|
44
45
|
import { jsenvPluginJsModuleFallback } from "@jsenv/plugin-transpilation";
|
|
@@ -117,6 +118,12 @@ import { jsenvPluginMappings } from "./jsenv_plugin_mappings.js";
|
|
|
117
118
|
* Relative URL where this entry point will be written in the build directory
|
|
118
119
|
* @param {object} [entryPoint.runtimeCompat]
|
|
119
120
|
* Runtime compatibility configuration for this entry point
|
|
121
|
+
* @param {boolean} [entryPoint.dev=false]
|
|
122
|
+
* Produce a dev-flavored build: import.meta.dev is replaced by true
|
|
123
|
+
* (instead of undefined) and dependencies resolve their "development"
|
|
124
|
+
* package export condition (instead of "production"). Meant for packages
|
|
125
|
+
* publishing two flavors, the dev one exposed to consumers via the
|
|
126
|
+
* "development" condition in their package.json "exports"
|
|
120
127
|
* @param {string} [entryPoint.assetsDirectory]
|
|
121
128
|
* Directory where asset files will be written for this entry point
|
|
122
129
|
* @param {string|url} [entryPoint.base]
|
|
@@ -240,12 +247,10 @@ entryPoints: {
|
|
|
240
247
|
let runtimeType;
|
|
241
248
|
{
|
|
242
249
|
if (isBareSpecifier(key)) {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
"import",
|
|
248
|
-
];
|
|
250
|
+
// "development" is not resolved here on purpose: it points at a
|
|
251
|
+
// package's dev-flavored dist, which is not a build input;
|
|
252
|
+
// "dev:*" points at sources, which are.
|
|
253
|
+
const packageConditions = ["dev:*", "node", "import"];
|
|
249
254
|
try {
|
|
250
255
|
const { url, type } = applyNodeEsmResolution({
|
|
251
256
|
conditions: packageConditions,
|
|
@@ -854,10 +859,31 @@ entryPoints: {
|
|
|
854
859
|
).href;
|
|
855
860
|
packageSideEffectUrlSet.add(sideEffectFileUrl);
|
|
856
861
|
}
|
|
862
|
+
// A side effect entry is outdated (and pruned) only when this build
|
|
863
|
+
// owns it: it is inside the build directory AND the clean patterns
|
|
864
|
+
// would remove it. A file kept by buildDirectoryCleanPatterns
|
|
865
|
+
// belongs to another build writing inside this directory (e.g. a
|
|
866
|
+
// dev-flavored build in a subdirectory, possibly running in
|
|
867
|
+
// parallel): its entries must be preserved, like files outside the
|
|
868
|
+
// build directory.
|
|
869
|
+
const cleanAssociations = URL_META.resolveAssociations(
|
|
870
|
+
{ remove: buildDirectoryCleanPatterns },
|
|
871
|
+
buildDirectoryUrl,
|
|
872
|
+
);
|
|
873
|
+
const isOwnedByThisBuild = (url) => {
|
|
874
|
+
if (!urlIsOrIsInsideOf(url, buildDirectoryUrl)) {
|
|
875
|
+
return false;
|
|
876
|
+
}
|
|
877
|
+
const { remove } = URL_META.applyAssociations({
|
|
878
|
+
url,
|
|
879
|
+
associations: cleanAssociations,
|
|
880
|
+
});
|
|
881
|
+
return Boolean(remove);
|
|
882
|
+
};
|
|
857
883
|
let hasSomeOutdatedSideEffectUrl = false;
|
|
858
884
|
for (const packageSideEffectUrl of packageSideEffectUrlSet) {
|
|
859
885
|
if (
|
|
860
|
-
|
|
886
|
+
isOwnedByThisBuild(packageSideEffectUrl) &&
|
|
861
887
|
!buildSideEffectUrlSet.has(packageSideEffectUrl)
|
|
862
888
|
) {
|
|
863
889
|
hasSomeOutdatedSideEffectUrl = true;
|
|
@@ -979,6 +1005,7 @@ entryPoints: {
|
|
|
979
1005
|
const entryPointDefaultParams = {
|
|
980
1006
|
buildRelativeUrl: undefined,
|
|
981
1007
|
mode: undefined,
|
|
1008
|
+
dev: false,
|
|
982
1009
|
runtimeCompat: undefined,
|
|
983
1010
|
plugins: [],
|
|
984
1011
|
mappings: undefined,
|
|
@@ -1034,6 +1061,7 @@ const prepareEntryPointBuild = async (
|
|
|
1034
1061
|
let {
|
|
1035
1062
|
buildRelativeUrl,
|
|
1036
1063
|
mode,
|
|
1064
|
+
dev,
|
|
1037
1065
|
runtimeCompat,
|
|
1038
1066
|
plugins,
|
|
1039
1067
|
mappings,
|
|
@@ -1214,6 +1242,7 @@ const prepareEntryPointBuild = async (
|
|
|
1214
1242
|
packageDirectory,
|
|
1215
1243
|
rootDirectoryUrl: sourceDirectoryUrl,
|
|
1216
1244
|
runtimeCompat,
|
|
1245
|
+
dev,
|
|
1217
1246
|
referenceAnalysis,
|
|
1218
1247
|
nodeEsmResolution,
|
|
1219
1248
|
packageConditions,
|
|
@@ -7,13 +7,18 @@
|
|
|
7
7
|
* - left as is to be evaluated to undefined (import.meta.build but it's the dev server)
|
|
8
8
|
* - replaced by undefined (import.meta.dev but it's build; the goal is to ensure it's tree-shaked)
|
|
9
9
|
*
|
|
10
|
+
* A build can also be dev-flavored (build({ entryPoints: { "./file.js": { dev: true } } })):
|
|
11
|
+
* import.meta.dev is then replaced by true, so a package can publish a build
|
|
12
|
+
* that keeps its dev-only code, exposed to consumers via the "development"
|
|
13
|
+
* package export condition.
|
|
14
|
+
*
|
|
10
15
|
* TODO: ideally during dev we would keep import.meta.dev and ensure we set it to true rather than replacing it with true?
|
|
11
16
|
*/
|
|
12
17
|
|
|
13
18
|
import { getImportMetaPropertyName, visitJsAst } from "@jsenv/ast";
|
|
14
19
|
import { createMagicSource } from "@jsenv/sourcemap";
|
|
15
20
|
|
|
16
|
-
export const jsenvPluginImportMetaScenarios = () => {
|
|
21
|
+
export const jsenvPluginImportMetaScenarios = ({ dev } = {}) => {
|
|
17
22
|
return {
|
|
18
23
|
name: "jsenv:import_meta_scenario",
|
|
19
24
|
appliesDuring: "*",
|
|
@@ -43,17 +48,19 @@ export const jsenvPluginImportMetaScenarios = () => {
|
|
|
43
48
|
}
|
|
44
49
|
},
|
|
45
50
|
});
|
|
46
|
-
const
|
|
51
|
+
const devNodes = importMetaScenarioNodes.dev;
|
|
52
|
+
const buildNodes = importMetaScenarioNodes.build;
|
|
47
53
|
const replacements = [];
|
|
48
54
|
const replace = (node, value) => {
|
|
49
55
|
replacements.push({ node, value });
|
|
50
56
|
};
|
|
51
57
|
if (urlInfo.context.build) {
|
|
52
58
|
// during build ensure replacement for tree-shaking
|
|
53
|
-
|
|
54
|
-
|
|
59
|
+
// (or for keeping dev code when the build is dev-flavored)
|
|
60
|
+
devNodes.forEach((node) => {
|
|
61
|
+
replace(node, dev ? "true" : "undefined");
|
|
55
62
|
});
|
|
56
|
-
|
|
63
|
+
buildNodes.forEach((node) => {
|
|
57
64
|
replace(node, "true");
|
|
58
65
|
});
|
|
59
66
|
} else {
|
|
@@ -61,7 +68,7 @@ export const jsenvPluginImportMetaScenarios = () => {
|
|
|
61
68
|
// it will be evaluated to undefined.
|
|
62
69
|
// Moreover it can be surprising to see some "undefined"
|
|
63
70
|
// when source file contains "import.meta.build"
|
|
64
|
-
|
|
71
|
+
devNodes.forEach((node) => {
|
|
65
72
|
replace(node, "true");
|
|
66
73
|
});
|
|
67
74
|
}
|
package/src/plugins/plugins.js
CHANGED
|
@@ -38,6 +38,9 @@ export const getCorePlugins = ({
|
|
|
38
38
|
runtimeCompat,
|
|
39
39
|
packageDirectory,
|
|
40
40
|
sourceFilesConfig,
|
|
41
|
+
// during build, a dev-flavored entry point sets dev: true
|
|
42
|
+
// (during dev the kitchen context already says so, no need to pass it)
|
|
43
|
+
dev,
|
|
41
44
|
|
|
42
45
|
referenceAnalysis = {},
|
|
43
46
|
nodeEsmResolution = {},
|
|
@@ -134,6 +137,7 @@ export const getCorePlugins = ({
|
|
|
134
137
|
resolutionConfig: nodeEsmResolution,
|
|
135
138
|
packageConditions,
|
|
136
139
|
packageConditionsConfig,
|
|
140
|
+
dev,
|
|
137
141
|
}),
|
|
138
142
|
]
|
|
139
143
|
: []),
|
|
@@ -151,7 +155,7 @@ export const getCorePlugins = ({
|
|
|
151
155
|
|
|
152
156
|
jsenvPluginImportMetaCss(),
|
|
153
157
|
jsenvPluginCommonJsGlobals(),
|
|
154
|
-
jsenvPluginImportMetaScenarios(),
|
|
158
|
+
jsenvPluginImportMetaScenarios({ dev }),
|
|
155
159
|
...(scenarioPlaceholders ? [jsenvPluginGlobalScenarios()] : []),
|
|
156
160
|
jsenvPluginNodeRuntime({ runtimeCompat }),
|
|
157
161
|
|
|
@@ -5,6 +5,7 @@ export const jsenvPluginNodeEsmResolution = ({
|
|
|
5
5
|
resolutionConfig = {},
|
|
6
6
|
packageConditions,
|
|
7
7
|
packageConditionsConfig = {},
|
|
8
|
+
dev,
|
|
8
9
|
}) => {
|
|
9
10
|
let nodeEsmResolverDefault;
|
|
10
11
|
const resolverMap = new Map();
|
|
@@ -26,6 +27,8 @@ export const jsenvPluginNodeEsmResolution = ({
|
|
|
26
27
|
packageDirectory,
|
|
27
28
|
runtimeCompat: kitchenContext.runtimeCompat,
|
|
28
29
|
rootDirectoryUrl: kitchenContext.rootDirectoryUrl,
|
|
30
|
+
dev: dev === undefined ? kitchenContext.dev : dev,
|
|
31
|
+
build: kitchenContext.build,
|
|
29
32
|
packageConditions,
|
|
30
33
|
packageConditionsConfig: {
|
|
31
34
|
...kitchenContext.packageConditionsConfig,
|
|
@@ -43,6 +46,8 @@ export const jsenvPluginNodeEsmResolution = ({
|
|
|
43
46
|
packageDirectory,
|
|
44
47
|
runtimeCompat: kitchenContext.runtimeCompat,
|
|
45
48
|
rootDirectoryUrl: kitchenContext.rootDirectoryUrl,
|
|
49
|
+
dev: dev === undefined ? kitchenContext.dev : dev,
|
|
50
|
+
build: kitchenContext.build,
|
|
46
51
|
// preservesSymlink: true,
|
|
47
52
|
packageConditions,
|
|
48
53
|
packageConditionsConfig: {
|
|
@@ -20,6 +20,8 @@ export const createNodeEsmResolver = ({
|
|
|
20
20
|
packageDirectory,
|
|
21
21
|
runtimeCompat,
|
|
22
22
|
rootDirectoryUrl,
|
|
23
|
+
dev,
|
|
24
|
+
build,
|
|
23
25
|
packageConditions = {},
|
|
24
26
|
packageConditionsConfig,
|
|
25
27
|
preservesSymlink,
|
|
@@ -37,6 +39,8 @@ export const createNodeEsmResolver = ({
|
|
|
37
39
|
packageConditionsConfig,
|
|
38
40
|
rootDirectoryUrl,
|
|
39
41
|
runtimeCompat,
|
|
42
|
+
dev,
|
|
43
|
+
build,
|
|
40
44
|
preservesSymlink,
|
|
41
45
|
},
|
|
42
46
|
);
|
|
@@ -198,6 +202,8 @@ const createBuildPackageConditions = (
|
|
|
198
202
|
packageConditionsConfig,
|
|
199
203
|
rootDirectoryUrl,
|
|
200
204
|
runtimeCompat,
|
|
205
|
+
dev,
|
|
206
|
+
build,
|
|
201
207
|
preservesSymlink,
|
|
202
208
|
},
|
|
203
209
|
) => {
|
|
@@ -287,8 +293,19 @@ const createBuildPackageConditions = (
|
|
|
287
293
|
};
|
|
288
294
|
|
|
289
295
|
const conditionDefaultResolvers = {
|
|
296
|
+
// "dev:*" conditions target packages under active development: they
|
|
297
|
+
// apply to files resolved outside node_modules (sources, workspace
|
|
298
|
+
// symlinks), never to installed packages.
|
|
290
299
|
"dev:*": devResolver,
|
|
291
|
-
"development"
|
|
300
|
+
// "development"/"production" follow the ecosystem semantics
|
|
301
|
+
// (Vite, webpack): they describe how the app runs, not where the
|
|
302
|
+
// package lives, so they apply to every package, node_modules
|
|
303
|
+
// included. Dev server resolves "development"; build resolves
|
|
304
|
+
// "production", unless the build is dev-flavored
|
|
305
|
+
// (build({ entryPoints: { "./file.js": { dev: true } } }))
|
|
306
|
+
// in which case it resolves "development" too.
|
|
307
|
+
"development": Boolean(dev),
|
|
308
|
+
"production": Boolean(build) && !dev,
|
|
292
309
|
"node": nodeRuntimeEnabled,
|
|
293
310
|
"browser": !nodeRuntimeEnabled,
|
|
294
311
|
"import": true,
|