@jsenv/core 41.5.2 → 41.5.4

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.
@@ -5589,7 +5589,10 @@ const decideLinkExpectedType = (linkReference, htmlUrlInfo) => {
5589
5589
  // - new SharedWorker()
5590
5590
  // - navigator.serviceWorker.register()
5591
5591
  const isWebWorkerEntryPointReference = (reference) => {
5592
- if (reference.subtype === "new_url_first_arg") {
5592
+ if (
5593
+ reference.subtype === "new_url_first_arg" ||
5594
+ reference.subtype === "import_meta_resolve"
5595
+ ) {
5593
5596
  return ["worker", "service_worker", "shared_worker"].includes(
5594
5597
  reference.expectedSubtype,
5595
5598
  );
@@ -6020,6 +6023,8 @@ const createNodeEsmResolver = ({
6020
6023
  packageDirectory,
6021
6024
  runtimeCompat,
6022
6025
  rootDirectoryUrl,
6026
+ dev,
6027
+ build,
6023
6028
  packageConditions = {},
6024
6029
  packageConditionsConfig,
6025
6030
  preservesSymlink,
@@ -6037,6 +6042,8 @@ const createNodeEsmResolver = ({
6037
6042
  packageConditionsConfig,
6038
6043
  rootDirectoryUrl,
6039
6044
  runtimeCompat,
6045
+ dev,
6046
+ build,
6040
6047
  preservesSymlink,
6041
6048
  },
6042
6049
  );
@@ -6198,6 +6205,8 @@ const createBuildPackageConditions = (
6198
6205
  packageConditionsConfig,
6199
6206
  rootDirectoryUrl,
6200
6207
  runtimeCompat,
6208
+ dev,
6209
+ build,
6201
6210
  preservesSymlink,
6202
6211
  },
6203
6212
  ) => {
@@ -6287,8 +6296,19 @@ const createBuildPackageConditions = (
6287
6296
  };
6288
6297
 
6289
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.
6290
6302
  "dev:*": devResolver,
6291
- "development": devResolver,
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,
6292
6312
  "node": nodeRuntimeEnabled,
6293
6313
  "browser": !nodeRuntimeEnabled,
6294
6314
  "import": true,
@@ -6502,6 +6522,7 @@ const jsenvPluginNodeEsmResolution = ({
6502
6522
  resolutionConfig = {},
6503
6523
  packageConditions,
6504
6524
  packageConditionsConfig = {},
6525
+ dev,
6505
6526
  }) => {
6506
6527
  let nodeEsmResolverDefault;
6507
6528
  const resolverMap = new Map();
@@ -6523,6 +6544,8 @@ const jsenvPluginNodeEsmResolution = ({
6523
6544
  packageDirectory,
6524
6545
  runtimeCompat: kitchenContext.runtimeCompat,
6525
6546
  rootDirectoryUrl: kitchenContext.rootDirectoryUrl,
6547
+ dev: dev === undefined ? kitchenContext.dev : dev,
6548
+ build: kitchenContext.build,
6526
6549
  packageConditions,
6527
6550
  packageConditionsConfig: {
6528
6551
  ...kitchenContext.packageConditionsConfig,
@@ -6540,6 +6563,8 @@ const jsenvPluginNodeEsmResolution = ({
6540
6563
  packageDirectory,
6541
6564
  runtimeCompat: kitchenContext.runtimeCompat,
6542
6565
  rootDirectoryUrl: kitchenContext.rootDirectoryUrl,
6566
+ dev: dev === undefined ? kitchenContext.dev : dev,
6567
+ build: kitchenContext.build,
6543
6568
  // preservesSymlink: true,
6544
6569
  packageConditions,
6545
6570
  packageConditionsConfig: {
@@ -8186,11 +8211,16 @@ const babelPluginMetadataExpressionPaths = (
8186
8211
  * - left as is to be evaluated to undefined (import.meta.build but it's the dev server)
8187
8212
  * - replaced by undefined (import.meta.dev but it's build; the goal is to ensure it's tree-shaked)
8188
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
+ *
8189
8219
  * TODO: ideally during dev we would keep import.meta.dev and ensure we set it to true rather than replacing it with true?
8190
8220
  */
8191
8221
 
8192
8222
 
8193
- const jsenvPluginImportMetaScenarios = () => {
8223
+ const jsenvPluginImportMetaScenarios = ({ dev } = {}) => {
8194
8224
  return {
8195
8225
  name: "jsenv:import_meta_scenario",
8196
8226
  appliesDuring: "*",
@@ -8220,17 +8250,19 @@ const jsenvPluginImportMetaScenarios = () => {
8220
8250
  }
8221
8251
  },
8222
8252
  });
8223
- const { dev, build } = importMetaScenarioNodes;
8253
+ const devNodes = importMetaScenarioNodes.dev;
8254
+ const buildNodes = importMetaScenarioNodes.build;
8224
8255
  const replacements = [];
8225
8256
  const replace = (node, value) => {
8226
8257
  replacements.push({ node, value });
8227
8258
  };
8228
8259
  if (urlInfo.context.build) {
8229
8260
  // during build ensure replacement for tree-shaking
8230
- dev.forEach((node) => {
8231
- replace(node, "undefined");
8261
+ // (or for keeping dev code when the build is dev-flavored)
8262
+ devNodes.forEach((node) => {
8263
+ replace(node, dev ? "true" : "undefined");
8232
8264
  });
8233
- build.forEach((node) => {
8265
+ buildNodes.forEach((node) => {
8234
8266
  replace(node, "true");
8235
8267
  });
8236
8268
  } else {
@@ -8238,7 +8270,7 @@ const jsenvPluginImportMetaScenarios = () => {
8238
8270
  // it will be evaluated to undefined.
8239
8271
  // Moreover it can be surprising to see some "undefined"
8240
8272
  // when source file contains "import.meta.build"
8241
- dev.forEach((node) => {
8273
+ devNodes.forEach((node) => {
8242
8274
  replace(node, "true");
8243
8275
  });
8244
8276
  }
@@ -10116,6 +10148,9 @@ const getCorePlugins = ({
10116
10148
  runtimeCompat,
10117
10149
  packageDirectory,
10118
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,
10119
10154
 
10120
10155
  referenceAnalysis = {},
10121
10156
  nodeEsmResolution = {},
@@ -10212,6 +10247,7 @@ const getCorePlugins = ({
10212
10247
  resolutionConfig: nodeEsmResolution,
10213
10248
  packageConditions,
10214
10249
  packageConditionsConfig,
10250
+ dev,
10215
10251
  }),
10216
10252
  ]
10217
10253
  : []),
@@ -10229,7 +10265,7 @@ const getCorePlugins = ({
10229
10265
 
10230
10266
  jsenvPluginImportMetaCss(),
10231
10267
  jsenvPluginCommonJsGlobals(),
10232
- jsenvPluginImportMetaScenarios(),
10268
+ jsenvPluginImportMetaScenarios({ dev }),
10233
10269
  ...(scenarioPlaceholders ? [jsenvPluginGlobalScenarios()] : []),
10234
10270
  jsenvPluginNodeRuntime({ runtimeCompat }),
10235
10271
 
@@ -12410,6 +12446,12 @@ const jsenvPluginMappings = (mappings) => {
12410
12446
  * Relative URL where this entry point will be written in the build directory
12411
12447
  * @param {object} [entryPoint.runtimeCompat]
12412
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"
12413
12455
  * @param {string} [entryPoint.assetsDirectory]
12414
12456
  * Directory where asset files will be written for this entry point
12415
12457
  * @param {string|url} [entryPoint.base]
@@ -12533,12 +12575,10 @@ entryPoints: {
12533
12575
  let runtimeType;
12534
12576
  {
12535
12577
  if (isBareSpecifier(key)) {
12536
- const packageConditions = [
12537
- "development",
12538
- "dev:*",
12539
- "node",
12540
- "import",
12541
- ];
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"];
12542
12582
  try {
12543
12583
  const { url, type } = applyNodeEsmResolution({
12544
12584
  conditions: packageConditions,
@@ -13270,6 +13310,7 @@ entryPoints: {
13270
13310
  const entryPointDefaultParams = {
13271
13311
  buildRelativeUrl: undefined,
13272
13312
  mode: undefined,
13313
+ dev: false,
13273
13314
  runtimeCompat: undefined,
13274
13315
  plugins: [],
13275
13316
  mappings: undefined,
@@ -13325,6 +13366,7 @@ const prepareEntryPointBuild = async (
13325
13366
  let {
13326
13367
  buildRelativeUrl,
13327
13368
  mode,
13369
+ dev,
13328
13370
  runtimeCompat,
13329
13371
  plugins,
13330
13372
  mappings,
@@ -13505,6 +13547,7 @@ const prepareEntryPointBuild = async (
13505
13547
  packageDirectory,
13506
13548
  rootDirectoryUrl: sourceDirectoryUrl,
13507
13549
  runtimeCompat,
13550
+ dev,
13508
13551
  referenceAnalysis,
13509
13552
  nodeEsmResolution,
13510
13553
  packageConditions,
@@ -2236,7 +2236,10 @@ const decideLinkExpectedType = (linkReference, htmlUrlInfo) => {
2236
2236
  // - new SharedWorker()
2237
2237
  // - navigator.serviceWorker.register()
2238
2238
  const isWebWorkerEntryPointReference = (reference) => {
2239
- if (reference.subtype === "new_url_first_arg") {
2239
+ if (
2240
+ reference.subtype === "new_url_first_arg" ||
2241
+ reference.subtype === "import_meta_resolve"
2242
+ ) {
2240
2243
  return ["worker", "service_worker", "shared_worker"].includes(
2241
2244
  reference.expectedSubtype,
2242
2245
  );
@@ -2686,6 +2689,8 @@ const createNodeEsmResolver = ({
2686
2689
  packageDirectory,
2687
2690
  runtimeCompat,
2688
2691
  rootDirectoryUrl,
2692
+ dev,
2693
+ build,
2689
2694
  packageConditions = {},
2690
2695
  packageConditionsConfig,
2691
2696
  preservesSymlink,
@@ -2703,6 +2708,8 @@ const createNodeEsmResolver = ({
2703
2708
  packageConditionsConfig,
2704
2709
  rootDirectoryUrl,
2705
2710
  runtimeCompat,
2711
+ dev,
2712
+ build,
2706
2713
  preservesSymlink,
2707
2714
  },
2708
2715
  );
@@ -2864,6 +2871,8 @@ const createBuildPackageConditions = (
2864
2871
  packageConditionsConfig,
2865
2872
  rootDirectoryUrl,
2866
2873
  runtimeCompat,
2874
+ dev,
2875
+ build,
2867
2876
  preservesSymlink,
2868
2877
  },
2869
2878
  ) => {
@@ -2953,8 +2962,19 @@ const createBuildPackageConditions = (
2953
2962
  };
2954
2963
 
2955
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.
2956
2968
  "dev:*": devResolver,
2957
- "development": devResolver,
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,
2958
2978
  "node": nodeRuntimeEnabled,
2959
2979
  "browser": !nodeRuntimeEnabled,
2960
2980
  "import": true,
@@ -3168,6 +3188,7 @@ const jsenvPluginNodeEsmResolution = ({
3168
3188
  resolutionConfig = {},
3169
3189
  packageConditions,
3170
3190
  packageConditionsConfig = {},
3191
+ dev,
3171
3192
  }) => {
3172
3193
  let nodeEsmResolverDefault;
3173
3194
  const resolverMap = new Map();
@@ -3189,6 +3210,8 @@ const jsenvPluginNodeEsmResolution = ({
3189
3210
  packageDirectory,
3190
3211
  runtimeCompat: kitchenContext.runtimeCompat,
3191
3212
  rootDirectoryUrl: kitchenContext.rootDirectoryUrl,
3213
+ dev: dev === undefined ? kitchenContext.dev : dev,
3214
+ build: kitchenContext.build,
3192
3215
  packageConditions,
3193
3216
  packageConditionsConfig: {
3194
3217
  ...kitchenContext.packageConditionsConfig,
@@ -3206,6 +3229,8 @@ const jsenvPluginNodeEsmResolution = ({
3206
3229
  packageDirectory,
3207
3230
  runtimeCompat: kitchenContext.runtimeCompat,
3208
3231
  rootDirectoryUrl: kitchenContext.rootDirectoryUrl,
3232
+ dev: dev === undefined ? kitchenContext.dev : dev,
3233
+ build: kitchenContext.build,
3209
3234
  // preservesSymlink: true,
3210
3235
  packageConditions,
3211
3236
  packageConditionsConfig: {
@@ -5991,11 +6016,16 @@ const babelPluginMetadataExpressionPaths = (
5991
6016
  * - left as is to be evaluated to undefined (import.meta.build but it's the dev server)
5992
6017
  * - replaced by undefined (import.meta.dev but it's build; the goal is to ensure it's tree-shaked)
5993
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
+ *
5994
6024
  * TODO: ideally during dev we would keep import.meta.dev and ensure we set it to true rather than replacing it with true?
5995
6025
  */
5996
6026
 
5997
6027
 
5998
- const jsenvPluginImportMetaScenarios = () => {
6028
+ const jsenvPluginImportMetaScenarios = ({ dev } = {}) => {
5999
6029
  return {
6000
6030
  name: "jsenv:import_meta_scenario",
6001
6031
  appliesDuring: "*",
@@ -6025,17 +6055,19 @@ const jsenvPluginImportMetaScenarios = () => {
6025
6055
  }
6026
6056
  },
6027
6057
  });
6028
- const { dev, build } = importMetaScenarioNodes;
6058
+ const devNodes = importMetaScenarioNodes.dev;
6059
+ const buildNodes = importMetaScenarioNodes.build;
6029
6060
  const replacements = [];
6030
6061
  const replace = (node, value) => {
6031
6062
  replacements.push({ node, value });
6032
6063
  };
6033
6064
  if (urlInfo.context.build) {
6034
6065
  // during build ensure replacement for tree-shaking
6035
- dev.forEach((node) => {
6036
- replace(node, "undefined");
6066
+ // (or for keeping dev code when the build is dev-flavored)
6067
+ devNodes.forEach((node) => {
6068
+ replace(node, dev ? "true" : "undefined");
6037
6069
  });
6038
- build.forEach((node) => {
6070
+ buildNodes.forEach((node) => {
6039
6071
  replace(node, "true");
6040
6072
  });
6041
6073
  } else {
@@ -6043,7 +6075,7 @@ const jsenvPluginImportMetaScenarios = () => {
6043
6075
  // it will be evaluated to undefined.
6044
6076
  // Moreover it can be surprising to see some "undefined"
6045
6077
  // when source file contains "import.meta.build"
6046
- dev.forEach((node) => {
6078
+ devNodes.forEach((node) => {
6047
6079
  replace(node, "true");
6048
6080
  });
6049
6081
  }
@@ -7981,6 +8013,9 @@ const getCorePlugins = ({
7981
8013
  runtimeCompat,
7982
8014
  packageDirectory,
7983
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,
7984
8019
 
7985
8020
  referenceAnalysis = {},
7986
8021
  nodeEsmResolution = {},
@@ -8077,6 +8112,7 @@ const getCorePlugins = ({
8077
8112
  resolutionConfig: nodeEsmResolution,
8078
8113
  packageConditions,
8079
8114
  packageConditionsConfig,
8115
+ dev,
8080
8116
  }),
8081
8117
  ]
8082
8118
  : []),
@@ -8094,7 +8130,7 @@ const getCorePlugins = ({
8094
8130
 
8095
8131
  jsenvPluginImportMetaCss(),
8096
8132
  jsenvPluginCommonJsGlobals(),
8097
- jsenvPluginImportMetaScenarios(),
8133
+ jsenvPluginImportMetaScenarios({ dev }),
8098
8134
  ...(scenarioPlaceholders ? [jsenvPluginGlobalScenarios()] : []),
8099
8135
  jsenvPluginNodeRuntime({ runtimeCompat }),
8100
8136
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/core",
3
- "version": "41.5.2",
3
+ "version": "41.5.4",
4
4
  "type": "module",
5
5
  "description": "Tool to develop, test and build js projects",
6
6
  "repository": {
@@ -73,12 +73,12 @@
73
73
  "test:snapshot_clear": "npx @jsenv/filesystem clear **/tests/**/side_effects/"
74
74
  },
75
75
  "dependencies": {
76
- "@jsenv/ast": "6.10.0",
77
- "@jsenv/js-module-fallback": "1.6.1",
76
+ "@jsenv/ast": "6.10.1",
77
+ "@jsenv/js-module-fallback": "1.6.2",
78
78
  "@jsenv/plugin-bundling": "2.10.20",
79
- "@jsenv/plugin-minification": "1.7.14",
80
- "@jsenv/plugin-supervisor": "1.8.16",
81
- "@jsenv/plugin-transpilation": "1.7.1",
79
+ "@jsenv/plugin-minification": "1.7.15",
80
+ "@jsenv/plugin-supervisor": "1.8.17",
81
+ "@jsenv/plugin-transpilation": "1.7.2",
82
82
  "@jsenv/server": "17.6.3",
83
83
  "@jsenv/sourcemap": "1.4.3",
84
84
  "react-table": "7.8.0"
@@ -117,6 +117,12 @@ import { jsenvPluginMappings } from "./jsenv_plugin_mappings.js";
117
117
  * Relative URL where this entry point will be written in the build directory
118
118
  * @param {object} [entryPoint.runtimeCompat]
119
119
  * Runtime compatibility configuration for this entry point
120
+ * @param {boolean} [entryPoint.dev=false]
121
+ * Produce a dev-flavored build: import.meta.dev is replaced by true
122
+ * (instead of undefined) and dependencies resolve their "development"
123
+ * package export condition (instead of "production"). Meant for packages
124
+ * publishing two flavors, the dev one exposed to consumers via the
125
+ * "development" condition in their package.json "exports"
120
126
  * @param {string} [entryPoint.assetsDirectory]
121
127
  * Directory where asset files will be written for this entry point
122
128
  * @param {string|url} [entryPoint.base]
@@ -240,12 +246,10 @@ entryPoints: {
240
246
  let runtimeType;
241
247
  {
242
248
  if (isBareSpecifier(key)) {
243
- const packageConditions = [
244
- "development",
245
- "dev:*",
246
- "node",
247
- "import",
248
- ];
249
+ // "development" is not resolved here on purpose: it points at a
250
+ // package's dev-flavored dist, which is not a build input;
251
+ // "dev:*" points at sources, which are.
252
+ const packageConditions = ["dev:*", "node", "import"];
249
253
  try {
250
254
  const { url, type } = applyNodeEsmResolution({
251
255
  conditions: packageConditions,
@@ -979,6 +983,7 @@ entryPoints: {
979
983
  const entryPointDefaultParams = {
980
984
  buildRelativeUrl: undefined,
981
985
  mode: undefined,
986
+ dev: false,
982
987
  runtimeCompat: undefined,
983
988
  plugins: [],
984
989
  mappings: undefined,
@@ -1034,6 +1039,7 @@ const prepareEntryPointBuild = async (
1034
1039
  let {
1035
1040
  buildRelativeUrl,
1036
1041
  mode,
1042
+ dev,
1037
1043
  runtimeCompat,
1038
1044
  plugins,
1039
1045
  mappings,
@@ -1214,6 +1220,7 @@ const prepareEntryPointBuild = async (
1214
1220
  packageDirectory,
1215
1221
  rootDirectoryUrl: sourceDirectoryUrl,
1216
1222
  runtimeCompat,
1223
+ dev,
1217
1224
  referenceAnalysis,
1218
1225
  nodeEsmResolution,
1219
1226
  packageConditions,
@@ -3,7 +3,10 @@
3
3
  // - new SharedWorker()
4
4
  // - navigator.serviceWorker.register()
5
5
  export const isWebWorkerEntryPointReference = (reference) => {
6
- if (reference.subtype === "new_url_first_arg") {
6
+ if (
7
+ reference.subtype === "new_url_first_arg" ||
8
+ reference.subtype === "import_meta_resolve"
9
+ ) {
7
10
  return ["worker", "service_worker", "shared_worker"].includes(
8
11
  reference.expectedSubtype,
9
12
  );
@@ -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 { dev, build } = importMetaScenarioNodes;
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
- dev.forEach((node) => {
54
- replace(node, "undefined");
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
- build.forEach((node) => {
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
- dev.forEach((node) => {
71
+ devNodes.forEach((node) => {
65
72
  replace(node, "true");
66
73
  });
67
74
  }
@@ -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": devResolver,
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,