@jsenv/core 35.0.5 → 36.0.1

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.
Files changed (50) hide show
  1. package/README.md +1 -1
  2. package/dist/js/inline_content.js +5 -4
  3. package/dist/jsenv_core.js +1127 -1497
  4. package/package.json +8 -8
  5. package/src/build/build.js +49 -41
  6. package/src/dev/file_service.js +7 -17
  7. package/src/dev/start_dev_server.js +12 -7
  8. package/src/kitchen/kitchen.js +38 -19
  9. package/src/kitchen/url_graph.js +1 -1
  10. package/src/plugins/autoreload/jsenv_plugin_hmr.js +2 -2
  11. package/src/plugins/file_urls/jsenv_plugin_file_urls.js +4 -4
  12. package/src/plugins/http_urls/jsenv_plugin_http_urls.js +1 -1
  13. package/src/plugins/importmap/jsenv_plugin_importmap.js +1 -1
  14. package/src/plugins/inlining/jsenv_plugin_inlining.js +1 -1
  15. package/src/plugins/inlining/jsenv_plugin_inlining_as_data_url.js +13 -2
  16. package/src/plugins/plugin_controller.js +19 -10
  17. package/src/plugins/plugins.js +21 -25
  18. package/src/plugins/{url_analysis/css/css_urls.js → reference_analysis/css/jsenv_plugin_css_reference_analysis.js} +11 -1
  19. package/src/plugins/{inline_content_analysis/jsenv_plugin_data_urls.js → reference_analysis/data_urls/jsenv_plugin_data_urls_analysis.js} +19 -19
  20. package/src/plugins/reference_analysis/directory/jsenv_plugin_directory_reference_analysis.js +51 -0
  21. package/src/plugins/reference_analysis/html/jsenv_plugin_html_reference_analysis.js +429 -0
  22. package/src/plugins/reference_analysis/inline_content.js +7 -0
  23. package/src/plugins/reference_analysis/js/jsenv_plugin_js_reference_analysis.js +161 -0
  24. package/src/plugins/reference_analysis/jsenv_plugin_reference_analysis.js +120 -0
  25. package/src/plugins/{url_analysis → reference_analysis}/jsenv_plugin_reference_expected_types.js +19 -12
  26. package/src/plugins/{url_analysis/webmanifest/webmanifest_urls.js → reference_analysis/webmanifest/jsenv_plugin_webmanifest_reference_analysis.js} +13 -1
  27. package/src/plugins/resolution_node_esm/jsenv_plugin_node_esm_resolution.js +74 -0
  28. package/src/plugins/{url_resolution → resolution_node_esm}/node_esm_resolver.js +8 -0
  29. package/src/plugins/resolution_web/jsenv_plugin_web_resolution.js +45 -0
  30. package/src/plugins/transpilation/as_js_module/jsenv_plugin_as_js_module.js +1 -1
  31. package/src/plugins/transpilation/babel/jsenv_plugin_babel.js +1 -1
  32. package/src/plugins/transpilation/import_assertions/jsenv_plugin_import_assertions.js +4 -6
  33. package/src/plugins/transpilation/js_module_fallback/jsenv_plugin_js_module_conversion.js +1 -1
  34. package/src/plugins/transpilation/js_module_fallback/jsenv_plugin_js_module_fallback_inside_html.js +4 -6
  35. package/src/plugins/transpilation/js_module_fallback/jsenv_plugin_js_module_fallback_on_workers.js +1 -1
  36. package/src/plugins/url_type_from_reference.js +13 -0
  37. package/src/plugins/{url_version/jsenv_plugin_url_version.js → version_search_param/jsenv_plugin_version_search_param.js} +4 -4
  38. package/dist/html/explorer.html +0 -559
  39. package/dist/other/jsenv.png +0 -0
  40. package/src/plugins/explorer/client/explorer.html +0 -608
  41. package/src/plugins/explorer/client/jsenv.png +0 -0
  42. package/src/plugins/explorer/jsenv_plugin_explorer.js +0 -86
  43. package/src/plugins/inline_content_analysis/client/inline_content.js +0 -6
  44. package/src/plugins/inline_content_analysis/jsenv_plugin_html_inline_content_analysis.js +0 -206
  45. package/src/plugins/inline_content_analysis/jsenv_plugin_inline_content_analysis.js +0 -34
  46. package/src/plugins/inline_content_analysis/jsenv_plugin_js_inline_content_analysis.js +0 -314
  47. package/src/plugins/url_analysis/html/html_urls.js +0 -313
  48. package/src/plugins/url_analysis/js/js_urls.js +0 -65
  49. package/src/plugins/url_analysis/jsenv_plugin_url_analysis.js +0 -116
  50. package/src/plugins/url_resolution/jsenv_plugin_url_resolution.js +0 -140
@@ -1,206 +0,0 @@
1
- /*
2
- * This plugin ensure content inlined inside HTML is cooked (inline <script> for instance)
3
- * For <script hot-accept> the script content will be moved to a virtual file
4
- * to enable hot reloading
5
- */
6
-
7
- import { generateInlineContentUrl } from "@jsenv/urls";
8
- import {
9
- parseHtmlString,
10
- stringifyHtmlAst,
11
- visitHtmlNodes,
12
- getHtmlNodeText,
13
- getHtmlNodePosition,
14
- analyzeScriptNode,
15
- setHtmlNodeAttributes,
16
- setHtmlNodeText,
17
- removeHtmlNodeText,
18
- getHtmlNodeAttribute,
19
- } from "@jsenv/ast";
20
- import { CONTENT_TYPE } from "@jsenv/utils/src/content_type/content_type.js";
21
-
22
- export const jsenvPluginHtmlInlineContentAnalysis = ({
23
- analyzeConvertedScripts,
24
- }) => {
25
- const cookInlineContent = async ({
26
- context,
27
- inlineContentUrlInfo,
28
- inlineContentReference,
29
- }) => {
30
- try {
31
- await context.cook(inlineContentUrlInfo, {
32
- reference: inlineContentReference,
33
- });
34
- } catch (e) {
35
- if (e.code === "PARSE_ERROR") {
36
- // When something like <style> or <script> contains syntax error
37
- // the HTML in itself it still valid
38
- // keep the syntax error and continue with the HTML
39
- const messageStart =
40
- inlineContentUrlInfo.type === "css"
41
- ? `Syntax error on css declared inside <style>`
42
- : `Syntax error on js declared inside <script>`;
43
-
44
- context.logger.error(`${messageStart}: ${e.cause.reasonCode}
45
- ${e.traceMessage}`);
46
- } else {
47
- throw e;
48
- }
49
- }
50
- };
51
-
52
- return {
53
- name: "jsenv:html_inline_content_analysis",
54
- appliesDuring: "*",
55
- transformUrlContent: {
56
- html: async (urlInfo, context) => {
57
- const htmlAst = parseHtmlString(urlInfo.content);
58
- const mutations = [];
59
- const actions = [];
60
- visitHtmlNodes(htmlAst, {
61
- style: (styleNode) => {
62
- const styleNodeText = getHtmlNodeText(styleNode);
63
- if (!styleNodeText) {
64
- return;
65
- }
66
- const { line, column, lineEnd, columnEnd, isOriginal } =
67
- getHtmlNodePosition(styleNode, {
68
- preferOriginal: true,
69
- });
70
- const inlineStyleUrl = generateInlineContentUrl({
71
- url: urlInfo.url,
72
- extension: ".css",
73
- line,
74
- column,
75
- lineEnd,
76
- columnEnd,
77
- });
78
- const debug =
79
- getHtmlNodeAttribute(styleNode, "jsenv-debug") !== undefined;
80
- const [inlineStyleReference, inlineStyleUrlInfo] =
81
- context.referenceUtils.foundInline({
82
- node: styleNode,
83
- type: "style",
84
- expectedType: "css",
85
- isOriginalPosition: isOriginal,
86
- // we remove 1 to the line because imagine the following html:
87
- // <style>body { color: red; }</style>
88
- // -> content starts same line as <style>
89
- specifierLine: line - 1,
90
- specifierColumn: column,
91
- specifier: inlineStyleUrl,
92
- contentType: "text/css",
93
- content: styleNodeText,
94
- debug,
95
- });
96
- actions.push(async () => {
97
- await cookInlineContent({
98
- context,
99
- inlineContentUrlInfo: inlineStyleUrlInfo,
100
- inlineContentReference: inlineStyleReference,
101
- });
102
- });
103
- mutations.push(() => {
104
- setHtmlNodeText(styleNode, inlineStyleUrlInfo.content, {
105
- indentation: false, // indentation would decrease strack trace precision
106
- });
107
- setHtmlNodeAttributes(styleNode, {
108
- "jsenv-cooked-by": "jsenv:html_inline_content_analysis",
109
- });
110
- });
111
- },
112
- script: (scriptNode) => {
113
- const scriptNodeText = getHtmlNodeText(scriptNode);
114
- if (!scriptNodeText) {
115
- return;
116
- }
117
- // If the inline script was already handled by an other plugin, ignore it
118
- // - we want to preserve inline scripts generated by html supervisor during dev
119
- // - we want to avoid cooking twice a script during build
120
- if (
121
- !analyzeConvertedScripts &&
122
- getHtmlNodeAttribute(scriptNode, "jsenv-injected-by") ===
123
- "jsenv:js_module_fallback"
124
- ) {
125
- return;
126
- }
127
-
128
- const hotAccept =
129
- getHtmlNodeAttribute(scriptNode, "hot-accept") !== undefined;
130
- const { type, contentType, extension } =
131
- analyzeScriptNode(scriptNode);
132
- const { line, column, lineEnd, columnEnd, isOriginal } =
133
- getHtmlNodePosition(scriptNode, {
134
- preferOriginal: true,
135
- });
136
- let inlineScriptUrl = generateInlineContentUrl({
137
- url: urlInfo.url,
138
- extension: extension || CONTENT_TYPE.asFileExtension(contentType),
139
- line,
140
- column,
141
- lineEnd,
142
- columnEnd,
143
- });
144
- const debug =
145
- getHtmlNodeAttribute(scriptNode, "jsenv-debug") !== undefined;
146
- const [inlineScriptReference, inlineScriptUrlInfo] =
147
- context.referenceUtils.foundInline({
148
- node: scriptNode,
149
- type: "script",
150
- expectedType: type,
151
- // we remove 1 to the line because imagine the following html:
152
- // <script>console.log('ok')</script>
153
- // -> content starts same line as <script>
154
- specifierLine: line - 1,
155
- specifierColumn: column,
156
- isOriginalPosition: isOriginal,
157
- specifier: inlineScriptUrl,
158
- contentType,
159
- content: scriptNodeText,
160
- debug,
161
- });
162
- actions.push(async () => {
163
- await cookInlineContent({
164
- context,
165
- inlineContentUrlInfo: inlineScriptUrlInfo,
166
- inlineContentReference: inlineScriptReference,
167
- });
168
- mutations.push(() => {
169
- const attributes = {
170
- "jsenv-cooked-by": "jsenv:html_inline_content_analysis",
171
- // 1. <script type="jsx"> becomes <script>
172
- // 2. <script type="module/jsx"> becomes <script type="module">
173
- ...(extension
174
- ? { type: type === "js_module" ? "module" : undefined }
175
- : {}),
176
- };
177
- if (hotAccept) {
178
- removeHtmlNodeText(scriptNode);
179
- setHtmlNodeAttributes(scriptNode, {
180
- ...attributes,
181
- });
182
- } else {
183
- setHtmlNodeText(scriptNode, inlineScriptUrlInfo.content, {
184
- indentation: false, // indentation would decrease stack trace precision
185
- });
186
- setHtmlNodeAttributes(scriptNode, {
187
- ...attributes,
188
- });
189
- }
190
- });
191
- });
192
- },
193
- });
194
- if (actions.length > 0) {
195
- await Promise.all(actions.map((action) => action()));
196
- }
197
- if (mutations.length === 0) {
198
- return null;
199
- }
200
- mutations.forEach((mutation) => mutation());
201
- const htmlModified = stringifyHtmlAst(htmlAst);
202
- return htmlModified;
203
- },
204
- },
205
- };
206
- };
@@ -1,34 +0,0 @@
1
- import { jsenvPluginHtmlInlineContentAnalysis } from "./jsenv_plugin_html_inline_content_analysis.js";
2
- import { jsenvPluginJsInlineContentAnalysis } from "./jsenv_plugin_js_inline_content_analysis.js";
3
- import { jsenvPluginDataUrls } from "./jsenv_plugin_data_urls.js";
4
-
5
- export const jsenvPluginInlineContentAnalysis = ({
6
- fetchInlineUrls = true,
7
- analyzeConvertedScripts = false,
8
- allowEscapeForVersioning = false,
9
- } = {}) => {
10
- return [
11
- ...(fetchInlineUrls ? [jsenvPluginInlineContentFetcher()] : []),
12
- jsenvPluginHtmlInlineContentAnalysis({ analyzeConvertedScripts }),
13
- jsenvPluginJsInlineContentAnalysis({ allowEscapeForVersioning }),
14
- jsenvPluginDataUrls(),
15
- ];
16
- };
17
-
18
- const jsenvPluginInlineContentFetcher = () => {
19
- return {
20
- name: "jsenv:inline_content_fetcher",
21
- appliesDuring: "*",
22
- fetchUrlContent: (urlInfo) => {
23
- if (!urlInfo.isInline) {
24
- return null;
25
- }
26
- return {
27
- // we want to fetch the original content otherwise we might re-cook
28
- // content already cooked
29
- content: urlInfo.originalContent,
30
- contentType: urlInfo.contentType,
31
- };
32
- },
33
- };
34
- };
@@ -1,314 +0,0 @@
1
- import { generateInlineContentUrl } from "@jsenv/urls";
2
- import { createMagicSource } from "@jsenv/sourcemap";
3
- import { applyBabelPlugins } from "@jsenv/ast";
4
- import { CONTENT_TYPE } from "@jsenv/utils/src/content_type/content_type.js";
5
- import { JS_QUOTES } from "@jsenv/utils/src/string/js_quotes.js";
6
-
7
- export const jsenvPluginJsInlineContentAnalysis = ({
8
- allowEscapeForVersioning,
9
- }) => {
10
- const parseAndTransformInlineContentCalls = async (urlInfo, context) => {
11
- const inlineContentInfos = await parseJsInlineContentInfos({
12
- js: urlInfo.content,
13
- url: urlInfo.originalUrl,
14
- isJsModule: urlInfo.type === "js_module",
15
- });
16
- if (inlineContentInfos.length === 0) {
17
- return null;
18
- }
19
- const magicSource = createMagicSource(urlInfo.content);
20
- await inlineContentInfos.reduce(async (previous, inlineContentInfo) => {
21
- await previous;
22
- const inlineUrl = generateInlineContentUrl({
23
- url: urlInfo.url,
24
- extension: CONTENT_TYPE.asFileExtension(inlineContentInfo.contentType),
25
- line: inlineContentInfo.line,
26
- column: inlineContentInfo.column,
27
- lineEnd: inlineContentInfo.lineEnd,
28
- columnEnd: inlineContentInfo.columnEnd,
29
- });
30
- let { quote } = inlineContentInfo;
31
- if (
32
- quote === "`" &&
33
- !context.isSupportedOnCurrentClients("template_literals")
34
- ) {
35
- // if quote is "`" and template literals are not supported
36
- // we'll use a regular string (single or double quote)
37
- // when rendering the string
38
- quote = JS_QUOTES.pickBest(inlineContentInfo.content);
39
- }
40
- const [inlineReference, inlineUrlInfo] =
41
- context.referenceUtils.foundInline({
42
- type: "js_inline_content",
43
- subtype: inlineContentInfo.type, // "new_blob_first_arg", "new_inline_content_first_arg", "json_parse_first_arg"
44
- isOriginalPosition: urlInfo.content === urlInfo.originalContent,
45
- specifierLine: inlineContentInfo.line,
46
- specifierColumn: inlineContentInfo.column,
47
- specifier: inlineUrl,
48
- contentType: inlineContentInfo.contentType,
49
- content: inlineContentInfo.content,
50
- });
51
- inlineUrlInfo.jsQuote = quote;
52
- inlineReference.escape = (value) =>
53
- JS_QUOTES.escapeSpecialChars(value.slice(1, -1), { quote });
54
- await context.cook(inlineUrlInfo, { reference: inlineReference });
55
- magicSource.replace({
56
- start: inlineContentInfo.start,
57
- end: inlineContentInfo.end,
58
- replacement: JS_QUOTES.escapeSpecialChars(inlineUrlInfo.content, {
59
- quote,
60
- allowEscapeForVersioning,
61
- }),
62
- });
63
- }, Promise.resolve());
64
- return magicSource.toContentAndSourcemap();
65
- };
66
-
67
- return {
68
- name: "jsenv:js_inline_content_analysis",
69
- appliesDuring: "*",
70
- transformUrlContent: {
71
- js_classic: parseAndTransformInlineContentCalls,
72
- js_module: parseAndTransformInlineContentCalls,
73
- },
74
- };
75
- };
76
-
77
- const parseJsInlineContentInfos = async ({ js, url, isJsModule }) => {
78
- if (
79
- !js.includes("InlineContent") &&
80
- !js.includes("new Blob(") &&
81
- !js.includes("JSON.parse(")
82
- ) {
83
- return [];
84
- }
85
- const { metadata } = await applyBabelPlugins({
86
- babelPlugins: [babelPluginMetadataInlineContents],
87
- urlInfo: {
88
- originalUrl: url,
89
- type: isJsModule ? "js_module" : "js_classic",
90
- content: js,
91
- },
92
- });
93
- return metadata.inlineContentInfos;
94
- };
95
-
96
- const babelPluginMetadataInlineContents = () => {
97
- return {
98
- name: "metadata-inline-contents",
99
- visitor: {
100
- Program: (programPath, state) => {
101
- const inlineContentInfos = [];
102
- const onInlineContentInfo = (inlineContentInfo) => {
103
- inlineContentInfos.push(inlineContentInfo);
104
- };
105
- programPath.traverse({
106
- NewExpression: (path) => {
107
- if (isNewInlineContentCall(path)) {
108
- analyzeNewInlineContentCall(path.node, {
109
- onInlineContentInfo,
110
- });
111
- return;
112
- }
113
- if (isNewBlobCall(path.node)) {
114
- analyzeNewBlobCall(path.node, {
115
- onInlineContentInfo,
116
- });
117
- return;
118
- }
119
- },
120
- CallExpression: (path) => {
121
- const node = path.node;
122
- if (isJSONParseCall(node)) {
123
- analyzeJsonParseCall(node, {
124
- onInlineContentInfo,
125
- });
126
- }
127
- },
128
- });
129
- state.file.metadata.inlineContentInfos = inlineContentInfos;
130
- },
131
- },
132
- };
133
- };
134
-
135
- const isNewInlineContentCall = (path) => {
136
- const node = path.node;
137
- if (node.callee.type === "Identifier") {
138
- // terser rename import to use a shorter name
139
- const name = getOriginalName(path, node.callee.name);
140
- return name === "InlineContent";
141
- }
142
- if (node.callee.id && node.callee.id.type === "Identifier") {
143
- const name = getOriginalName(path, node.callee.id.name);
144
- return name === "InlineContent";
145
- }
146
- return false;
147
- };
148
- const analyzeNewInlineContentCall = (node, { onInlineContentInfo }) => {
149
- analyzeArguments({
150
- node,
151
- onInlineContentInfo,
152
- nodeHoldingContent: node.arguments[0],
153
- type: "new_inline_content_first_arg",
154
- });
155
- };
156
-
157
- const isNewBlobCall = (node) => {
158
- return node.callee.type === "Identifier" && node.callee.name === "Blob";
159
- };
160
- const analyzeNewBlobCall = (node, { onInlineContentInfo }) => {
161
- const firstArg = node.arguments[0];
162
- if (!firstArg) {
163
- return;
164
- }
165
- if (firstArg.type !== "ArrayExpression") {
166
- return;
167
- }
168
- if (firstArg.elements.length !== 1) {
169
- return;
170
- }
171
- analyzeArguments({
172
- node,
173
- onInlineContentInfo,
174
- nodeHoldingContent: firstArg.elements[0],
175
- type: "new_blob_first_arg",
176
- });
177
- };
178
-
179
- const analyzeArguments = ({
180
- node,
181
- onInlineContentInfo,
182
- nodeHoldingContent,
183
- type,
184
- }) => {
185
- if (node.arguments.length !== 2) {
186
- return;
187
- }
188
- const [, secondArg] = node.arguments;
189
- const typePropertyNode = getTypePropertyNode(secondArg);
190
- if (!typePropertyNode) {
191
- return;
192
- }
193
- const typePropertyValueNode = typePropertyNode.value;
194
- if (typePropertyValueNode.type !== "StringLiteral") {
195
- return;
196
- }
197
- const contentType = typePropertyValueNode.value;
198
- const contentDetails = extractContentDetails(nodeHoldingContent);
199
- if (contentDetails) {
200
- onInlineContentInfo({
201
- node: nodeHoldingContent,
202
- ...getNodePosition(nodeHoldingContent),
203
- type,
204
- contentType,
205
- ...contentDetails,
206
- });
207
- }
208
- };
209
- const extractContentDetails = (node) => {
210
- if (node.type === "StringLiteral") {
211
- return {
212
- nodeType: "StringLiteral",
213
- quote: node.extra.raw[0],
214
- content: node.value,
215
- };
216
- }
217
- if (node.type === "TemplateLiteral") {
218
- const quasis = node.quasis;
219
- if (quasis.length !== 1) {
220
- return null;
221
- }
222
- const templateElementNode = quasis[0];
223
- return {
224
- nodeType: "TemplateLiteral",
225
- quote: "`",
226
- content: templateElementNode.value.cooked,
227
- };
228
- }
229
- return null;
230
- };
231
-
232
- const isJSONParseCall = (node) => {
233
- const callee = node.callee;
234
- return (
235
- callee.type === "MemberExpression" &&
236
- callee.object.type === "Identifier" &&
237
- callee.object.name === "JSON" &&
238
- callee.property.type === "Identifier" &&
239
- callee.property.name === "parse"
240
- );
241
- };
242
- const analyzeJsonParseCall = (node, { onInlineContentInfo }) => {
243
- const firstArgNode = node.arguments[0];
244
- const contentDetails = extractContentDetails(firstArgNode);
245
- if (contentDetails) {
246
- onInlineContentInfo({
247
- node: firstArgNode,
248
- ...getNodePosition(firstArgNode),
249
- type: "json_parse_first_arg",
250
- contentType: "application/json",
251
- ...contentDetails,
252
- });
253
- }
254
- };
255
-
256
- const getNodePosition = (node) => {
257
- return {
258
- start: node.start,
259
- end: node.end,
260
- line: node.loc.start.line,
261
- column: node.loc.start.column,
262
- lineEnd: node.loc.end.line,
263
- columnEnd: node.loc.end.column,
264
- };
265
- };
266
- const getOriginalName = (path, name) => {
267
- const binding = path.scope.getBinding(name);
268
- if (!binding) {
269
- return name;
270
- }
271
- if (binding.path.type === "ImportSpecifier") {
272
- const importedName = binding.path.node.imported.name;
273
- if (name === importedName) {
274
- return name;
275
- }
276
- return getOriginalName(path, importedName);
277
- }
278
- if (binding.path.type === "VariableDeclarator") {
279
- const { node } = binding.path;
280
- const { init } = node;
281
- if (init && init.type === "Identifier") {
282
- const previousName = init.name;
283
- return getOriginalName(path, previousName);
284
- }
285
- if (node.id && node.id.type === "Identifier") {
286
- const { constantViolations } = binding;
287
- if (constantViolations && constantViolations.length > 0) {
288
- const lastViolation = constantViolations[constantViolations.length - 1];
289
- if (
290
- lastViolation &&
291
- lastViolation.node.type === "AssignmentExpression" &&
292
- lastViolation.node.right.type === "MemberExpression" &&
293
- lastViolation.node.right.property.type === "Identifier"
294
- ) {
295
- return lastViolation.node.right.property.name;
296
- }
297
- }
298
- }
299
- }
300
- return name;
301
- };
302
- const getTypePropertyNode = (node) => {
303
- if (node.type !== "ObjectExpression") {
304
- return null;
305
- }
306
- const { properties } = node;
307
- return properties.find((property) => {
308
- return (
309
- property.type === "ObjectProperty" &&
310
- property.key.type === "Identifier" &&
311
- property.key.name === "type"
312
- );
313
- });
314
- };