@jsenv/core 41.4.25 → 41.4.27

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.
@@ -1,4 +1,4 @@
1
- import { parseHtml, injectHtmlNodeAsEarlyAsPossible, createHtmlNode, stringifyHtmlAst, applyBabelPlugins, generateUrlForInlineContent, injectJsenvScript, parseJsWithAcorn, visitHtmlNodes, analyzeScriptNode, getHtmlNodeText, getHtmlNodeAttribute, getHtmlNodePosition, getUrlForContentInsideHtml, setHtmlNodeAttributes, setHtmlNodeText, parseCssUrls, getHtmlNodeAttributePosition, parseSrcSet, removeHtmlNodeText, parseJsUrls, getUrlForContentInsideJs, visitJsAst, getImportMetaPropertyName, visitJsAstUntil, analyzeLinkNode, findHtmlNode, removeHtmlNode, insertHtmlNodeAfter } from "@jsenv/ast";
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
2
  import { bundleJsModules, jsenvPluginBundling } from "@jsenv/plugin-bundling";
3
3
  import { jsenvPluginMinification } from "@jsenv/plugin-minification";
4
4
  import { jsenvPluginTranspilation, jsenvPluginJsModuleFallback } from "@jsenv/plugin-transpilation";
@@ -5689,9 +5689,17 @@ const parseAndTransformJsReferences = async (
5689
5689
 
5690
5690
  sequentialActions.push(async () => {
5691
5691
  await inlineUrlInfo.cook();
5692
- const replacement = JS_QUOTES.escapeSpecialChars(inlineUrlInfo.content, {
5693
- quote,
5694
- });
5692
+ const { substitutions } = inlineReferenceInfo;
5693
+ const replacement = substitutions
5694
+ ? // the expressions the template holds take their placeholder's place
5695
+ // back; a template literal is written whatever the runtime supports,
5696
+ // transpilation runs after and lowers it when it has to
5697
+ renderCssTemplateLiteral(inlineUrlInfo.content, substitutions)
5698
+ : JS_QUOTES.escapeSpecialChars(inlineUrlInfo.content, { quote });
5699
+ if (replacement === null) {
5700
+ // a placeholder did not survive: the source stays as it was written
5701
+ return;
5702
+ }
5695
5703
  magicSource.replace({
5696
5704
  start: inlineReferenceInfo.start,
5697
5705
  end: inlineReferenceInfo.end,
@@ -8314,6 +8322,12 @@ const jsenvPluginNodeRuntime = ({ runtimeCompat }) => {
8314
8322
  * import.meta.css = css;
8315
8323
  * ```
8316
8324
  *
8325
+ * "jsenv:js_reference_analysis" reads the css assigned here as an inline css and
8326
+ * sends it through the css pipeline: transpilation, url() resolution, minification,
8327
+ * comments. A "${}" standing where a css value stands is swapped for a placeholder
8328
+ * and put back afterwards; anywhere else it makes the template unreadable and the
8329
+ * css is shipped exactly as written, with everything the pipeline does lost.
8330
+ *
8317
8331
  */
8318
8332
 
8319
8333
 
@@ -8346,6 +8360,12 @@ const jsenvPluginImportMetaCss = () => {
8346
8360
  return null;
8347
8361
  }
8348
8362
  if (urlInfo.context.build) {
8363
+ if (hasModuleScopeAssignment(urlInfo.contentAst)) {
8364
+ urlInfo.contentSideEffects.push({
8365
+ has: true,
8366
+ reason: "import.meta.css assigned at module scope",
8367
+ });
8368
+ }
8349
8369
  const packageName = urlInfo.packageName;
8350
8370
  const packageDirectoryUrl =
8351
8371
  urlInfo.packageDirectoryUrl || urlInfo.context.rootDirectoryUrl;
@@ -8389,6 +8409,28 @@ const jsenvPluginImportMetaCss = () => {
8389
8409
  };
8390
8410
  };
8391
8411
 
8412
+ // A stylesheet assigned at module scope is adopted when the module runs: that
8413
+ // assignment IS the module's side effect. It has to be told, because a
8414
+ // package.json "sideEffects" list is authoritative once it exists — a module
8415
+ // it does not name is dropped whole when it is imported for nothing else, and
8416
+ // its css silently leaves the build.
8417
+ const hasModuleScopeAssignment = (ast) => {
8418
+ for (const node of ast.body) {
8419
+ if (node.type !== "ExpressionStatement") {
8420
+ continue;
8421
+ }
8422
+ const { expression } = node;
8423
+ if (
8424
+ expression.type === "AssignmentExpression" &&
8425
+ expression.operator === "=" &&
8426
+ getImportMetaPropertyName(expression.left) === "css"
8427
+ ) {
8428
+ return true;
8429
+ }
8430
+ }
8431
+ return false;
8432
+ };
8433
+
8392
8434
  const babelPluginRewriteImportMetaCssAssignment = (
8393
8435
  { types: t },
8394
8436
  { relativeUrl },
@@ -3,7 +3,7 @@ import { existsSync, readFileSync, realpathSync, readdirSync, lstatSync, statSyn
3
3
  import { pathToFileURL } from "node:url";
4
4
  import { urlToRelativeUrl, registerFileLifecycle, lookupPackageDirectory, readPackageAtOrNull, generateContentFrame, errorToHTML, DATA_URL, CONTENT_TYPE, normalizeImportMap, composeTwoImportMaps, resolveImport, JS_QUOTES, urlToExtension, urlToBasename, applyNodeEsmResolution, URL_META, readCustomConditionsFromProcessArgs, urlIsOrIsInsideOf, collectFiles, registerDirectoryLifecycle, readEntryStatSync, applyFileSystemMagicResolution, getExtensionsToTry, urlToFilename, asUrlWithoutSearch, ensurePathnameTrailingSlash, compareFileUrls, setUrlExtension, createDetailedMessage, stringifyUrlSite, injectQueryParamsIntoSpecifier, isSpecifierForNodeBuiltin, injectQueryParams, urlToFileSystemPath, writeFileSync, moveUrl, ensureWindowsDriveLetter, validateResponseIntegrity, setUrlFilename, getCallerPosition, asSpecifierWithoutSearch, bufferToEtag, isFileSystemPath, urlToPathname, setUrlBasename, createLogger, normalizeUrl, ANSI, RUNTIME_COMPAT, formatError, assertAndNormalizeDirectoryUrl, createTaskLog } from "./jsenv_core_packages.js";
5
5
  import { createPluginsController } from "@jsenv/server/src/plugins_controller.js";
6
- import { parseHtml, injectJsenvScript, stringifyHtmlAst, parseCssUrls, getHtmlNodeAttribute, getHtmlNodePosition, getHtmlNodeAttributePosition, setHtmlNodeAttributes, parseSrcSet, getUrlForContentInsideHtml, removeHtmlNodeText, setHtmlNodeText, getHtmlNodeText, analyzeScriptNode, visitHtmlNodes, parseJsUrls, getUrlForContentInsideJs, applyBabelPlugins, visitJsAst, getImportMetaPropertyName, visitJsAstUntil, analyzeLinkNode, injectHtmlNodeAsEarlyAsPossible, createHtmlNode, generateUrlForInlineContent, parseJsWithAcorn } from "@jsenv/ast";
6
+ import { parseHtml, injectJsenvScript, stringifyHtmlAst, parseCssUrls, getHtmlNodeAttribute, getHtmlNodePosition, getHtmlNodeAttributePosition, setHtmlNodeAttributes, parseSrcSet, getUrlForContentInsideHtml, removeHtmlNodeText, setHtmlNodeText, getHtmlNodeText, analyzeScriptNode, visitHtmlNodes, parseJsUrls, getUrlForContentInsideJs, renderCssTemplateLiteral, applyBabelPlugins, visitJsAst, getImportMetaPropertyName, visitJsAstUntil, analyzeLinkNode, injectHtmlNodeAsEarlyAsPossible, createHtmlNode, generateUrlForInlineContent, parseJsWithAcorn } from "@jsenv/ast";
7
7
  import { jsenvPluginSupervisor } from "@jsenv/plugin-supervisor";
8
8
  import { jsenvPluginTranspilation } from "@jsenv/plugin-transpilation";
9
9
  import { createMagicSource, composeTwoSourcemaps, generateSourcemapFileUrl, generateSourcemapDataUrl, SOURCEMAP } from "@jsenv/sourcemap";
@@ -2369,9 +2369,17 @@ const parseAndTransformJsReferences = async (
2369
2369
 
2370
2370
  sequentialActions.push(async () => {
2371
2371
  await inlineUrlInfo.cook();
2372
- const replacement = JS_QUOTES.escapeSpecialChars(inlineUrlInfo.content, {
2373
- quote,
2374
- });
2372
+ const { substitutions } = inlineReferenceInfo;
2373
+ const replacement = substitutions
2374
+ ? // the expressions the template holds take their placeholder's place
2375
+ // back; a template literal is written whatever the runtime supports,
2376
+ // transpilation runs after and lowers it when it has to
2377
+ renderCssTemplateLiteral(inlineUrlInfo.content, substitutions)
2378
+ : JS_QUOTES.escapeSpecialChars(inlineUrlInfo.content, { quote });
2379
+ if (replacement === null) {
2380
+ // a placeholder did not survive: the source stays as it was written
2381
+ return;
2382
+ }
2375
2383
  magicSource.replace({
2376
2384
  start: inlineReferenceInfo.start,
2377
2385
  end: inlineReferenceInfo.end,
@@ -6133,6 +6141,12 @@ const jsenvPluginNodeRuntime = ({ runtimeCompat }) => {
6133
6141
  * import.meta.css = css;
6134
6142
  * ```
6135
6143
  *
6144
+ * "jsenv:js_reference_analysis" reads the css assigned here as an inline css and
6145
+ * sends it through the css pipeline: transpilation, url() resolution, minification,
6146
+ * comments. A "${}" standing where a css value stands is swapped for a placeholder
6147
+ * and put back afterwards; anywhere else it makes the template unreadable and the
6148
+ * css is shipped exactly as written, with everything the pipeline does lost.
6149
+ *
6136
6150
  */
6137
6151
 
6138
6152
 
@@ -6165,6 +6179,12 @@ const jsenvPluginImportMetaCss = () => {
6165
6179
  return null;
6166
6180
  }
6167
6181
  if (urlInfo.context.build) {
6182
+ if (hasModuleScopeAssignment(urlInfo.contentAst)) {
6183
+ urlInfo.contentSideEffects.push({
6184
+ has: true,
6185
+ reason: "import.meta.css assigned at module scope",
6186
+ });
6187
+ }
6168
6188
  const packageName = urlInfo.packageName;
6169
6189
  const packageDirectoryUrl =
6170
6190
  urlInfo.packageDirectoryUrl || urlInfo.context.rootDirectoryUrl;
@@ -6208,6 +6228,28 @@ const jsenvPluginImportMetaCss = () => {
6208
6228
  };
6209
6229
  };
6210
6230
 
6231
+ // A stylesheet assigned at module scope is adopted when the module runs: that
6232
+ // assignment IS the module's side effect. It has to be told, because a
6233
+ // package.json "sideEffects" list is authoritative once it exists — a module
6234
+ // it does not name is dropped whole when it is imported for nothing else, and
6235
+ // its css silently leaves the build.
6236
+ const hasModuleScopeAssignment = (ast) => {
6237
+ for (const node of ast.body) {
6238
+ if (node.type !== "ExpressionStatement") {
6239
+ continue;
6240
+ }
6241
+ const { expression } = node;
6242
+ if (
6243
+ expression.type === "AssignmentExpression" &&
6244
+ expression.operator === "=" &&
6245
+ getImportMetaPropertyName(expression.left) === "css"
6246
+ ) {
6247
+ return true;
6248
+ }
6249
+ }
6250
+ return false;
6251
+ };
6252
+
6211
6253
  const babelPluginRewriteImportMetaCssAssignment = (
6212
6254
  { types: t },
6213
6255
  { relativeUrl },
@@ -11660,7 +11702,17 @@ const devServerPluginServeSourceFiles = ({
11660
11702
  // so it is derived from the url and cooked first.
11661
11703
  const inlineParentUrl = getInlineContentParentUrl(requestedUrl);
11662
11704
  if (inlineParentUrl) {
11663
- if (!kitchen.graph.getUrlInfo(inlineParentUrl)) {
11705
+ const inlineParentUrlInfo =
11706
+ kitchen.graph.getUrlInfo(inlineParentUrl);
11707
+ // A parent cooked before the file changed still holds the
11708
+ // references it had then; the inline content is as fresh as its
11709
+ // parent, so the parent is cooked again before being asked.
11710
+ if (
11711
+ !inlineParentUrlInfo ||
11712
+ inlineParentUrlInfo.content === undefined ||
11713
+ !inlineParentUrlInfo.contentFinalized ||
11714
+ !inlineParentUrlInfo.isValid()
11715
+ ) {
11664
11716
  const rootUrlInfo = kitchen.graph.rootUrlInfo;
11665
11717
  const inlineParentWebUrl = WEB_URL_CONVERTER.asWebUrl(
11666
11718
  inlineParentUrl,
@@ -11684,6 +11736,16 @@ const devServerPluginServeSourceFiles = ({
11684
11736
  request.resource,
11685
11737
  inlineParentUrl,
11686
11738
  );
11739
+ if (!reference) {
11740
+ // The parent does not hold that inline content: the script was
11741
+ // edited out of the html. What the graph kept under this url is
11742
+ // what the parent used to say, it must not be served.
11743
+ return {
11744
+ url: requestedUrl,
11745
+ status: 404,
11746
+ statusText: "no inline content at this position",
11747
+ };
11748
+ }
11687
11749
  }
11688
11750
  }
11689
11751
  if (reference) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/core",
3
- "version": "41.4.25",
3
+ "version": "41.4.27",
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.9.2",
77
- "@jsenv/js-module-fallback": "1.5.1",
76
+ "@jsenv/ast": "6.9.3",
77
+ "@jsenv/js-module-fallback": "1.5.2",
78
78
  "@jsenv/plugin-bundling": "2.10.18",
79
- "@jsenv/plugin-minification": "1.7.10",
80
- "@jsenv/plugin-supervisor": "1.8.13",
81
- "@jsenv/plugin-transpilation": "1.6.2",
79
+ "@jsenv/plugin-minification": "1.7.11",
80
+ "@jsenv/plugin-supervisor": "1.8.14",
81
+ "@jsenv/plugin-transpilation": "1.6.3",
82
82
  "@jsenv/server": "17.6.3",
83
83
  "@jsenv/sourcemap": "1.4.2",
84
84
  "react-table": "7.8.0"
@@ -287,7 +287,17 @@ export const devServerPluginServeSourceFiles = ({
287
287
  // so it is derived from the url and cooked first.
288
288
  const inlineParentUrl = getInlineContentParentUrl(requestedUrl);
289
289
  if (inlineParentUrl) {
290
- if (!kitchen.graph.getUrlInfo(inlineParentUrl)) {
290
+ const inlineParentUrlInfo =
291
+ kitchen.graph.getUrlInfo(inlineParentUrl);
292
+ // A parent cooked before the file changed still holds the
293
+ // references it had then; the inline content is as fresh as its
294
+ // parent, so the parent is cooked again before being asked.
295
+ if (
296
+ !inlineParentUrlInfo ||
297
+ inlineParentUrlInfo.content === undefined ||
298
+ !inlineParentUrlInfo.contentFinalized ||
299
+ !inlineParentUrlInfo.isValid()
300
+ ) {
291
301
  const rootUrlInfo = kitchen.graph.rootUrlInfo;
292
302
  const inlineParentWebUrl = WEB_URL_CONVERTER.asWebUrl(
293
303
  inlineParentUrl,
@@ -311,6 +321,16 @@ export const devServerPluginServeSourceFiles = ({
311
321
  request.resource,
312
322
  inlineParentUrl,
313
323
  );
324
+ if (!reference) {
325
+ // The parent does not hold that inline content: the script was
326
+ // edited out of the html. What the graph kept under this url is
327
+ // what the parent used to say, it must not be served.
328
+ return {
329
+ url: requestedUrl,
330
+ status: 404,
331
+ statusText: "no inline content at this position",
332
+ };
333
+ }
314
334
  }
315
335
  }
316
336
  if (reference) {
@@ -21,6 +21,12 @@
21
21
  * import.meta.css = css;
22
22
  * ```
23
23
  *
24
+ * "jsenv:js_reference_analysis" reads the css assigned here as an inline css and
25
+ * sends it through the css pipeline: transpilation, url() resolution, minification,
26
+ * comments. A "${}" standing where a css value stands is swapped for a placeholder
27
+ * and put back afterwards; anywhere else it makes the template unreadable and the
28
+ * css is shipped exactly as written, with everything the pipeline does lost.
29
+ *
24
30
  */
25
31
 
26
32
  import {
@@ -58,6 +64,12 @@ export const jsenvPluginImportMetaCss = () => {
58
64
  return null;
59
65
  }
60
66
  if (urlInfo.context.build) {
67
+ if (hasModuleScopeAssignment(urlInfo.contentAst)) {
68
+ urlInfo.contentSideEffects.push({
69
+ has: true,
70
+ reason: "import.meta.css assigned at module scope",
71
+ });
72
+ }
61
73
  const packageName = urlInfo.packageName;
62
74
  const packageDirectoryUrl =
63
75
  urlInfo.packageDirectoryUrl || urlInfo.context.rootDirectoryUrl;
@@ -101,6 +113,28 @@ export const jsenvPluginImportMetaCss = () => {
101
113
  };
102
114
  };
103
115
 
116
+ // A stylesheet assigned at module scope is adopted when the module runs: that
117
+ // assignment IS the module's side effect. It has to be told, because a
118
+ // package.json "sideEffects" list is authoritative once it exists — a module
119
+ // it does not name is dropped whole when it is imported for nothing else, and
120
+ // its css silently leaves the build.
121
+ const hasModuleScopeAssignment = (ast) => {
122
+ for (const node of ast.body) {
123
+ if (node.type !== "ExpressionStatement") {
124
+ continue;
125
+ }
126
+ const { expression } = node;
127
+ if (
128
+ expression.type === "AssignmentExpression" &&
129
+ expression.operator === "=" &&
130
+ getImportMetaPropertyName(expression.left) === "css"
131
+ ) {
132
+ return true;
133
+ }
134
+ }
135
+ return false;
136
+ };
137
+
104
138
  const babelPluginRewriteImportMetaCssAssignment = (
105
139
  { types: t },
106
140
  { relativeUrl },
@@ -1,4 +1,8 @@
1
- import { getUrlForContentInsideJs, parseJsUrls } from "@jsenv/ast";
1
+ import {
2
+ getUrlForContentInsideJs,
3
+ parseJsUrls,
4
+ renderCssTemplateLiteral,
5
+ } from "@jsenv/ast";
2
6
  import {
3
7
  isWebWorkerEntryPointReference,
4
8
  isWebWorkerUrlInfo,
@@ -70,9 +74,17 @@ const parseAndTransformJsReferences = async (
70
74
 
71
75
  sequentialActions.push(async () => {
72
76
  await inlineUrlInfo.cook();
73
- const replacement = JS_QUOTES.escapeSpecialChars(inlineUrlInfo.content, {
74
- quote,
75
- });
77
+ const { substitutions } = inlineReferenceInfo;
78
+ const replacement = substitutions
79
+ ? // the expressions the template holds take their placeholder's place
80
+ // back; a template literal is written whatever the runtime supports,
81
+ // transpilation runs after and lowers it when it has to
82
+ renderCssTemplateLiteral(inlineUrlInfo.content, substitutions)
83
+ : JS_QUOTES.escapeSpecialChars(inlineUrlInfo.content, { quote });
84
+ if (replacement === null) {
85
+ // a placeholder did not survive: the source stays as it was written
86
+ return;
87
+ }
76
88
  magicSource.replace({
77
89
  start: inlineReferenceInfo.start,
78
90
  end: inlineReferenceInfo.end,