@jsenv/core 36.3.0 → 37.0.0
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/js/autoreload.js +6 -5
- package/dist/js/import_meta_hot.js +4 -4
- package/dist/js/server_events_client.js +422 -304
- package/dist/jsenv_core.js +3819 -3256
- package/package.json +18 -17
- package/src/build/build.js +342 -658
- package/src/build/build_urls_generator.js +8 -8
- package/src/build/build_versions_manager.js +495 -0
- package/src/build/version_mappings_injection.js +27 -16
- package/src/dev/file_service.js +80 -91
- package/src/dev/start_dev_server.js +5 -3
- package/src/kitchen/errors.js +16 -16
- package/src/kitchen/fetched_content_compliance.js +4 -8
- package/src/kitchen/kitchen.js +367 -939
- package/src/kitchen/prepend_content.js +13 -35
- package/src/kitchen/url_graph/references.js +713 -0
- package/src/kitchen/url_graph/sort_by_dependencies.js +2 -2
- package/src/kitchen/url_graph/url_content.js +96 -0
- package/src/kitchen/url_graph/url_graph.js +439 -0
- package/src/kitchen/url_graph/url_graph_report.js +6 -4
- package/src/kitchen/url_graph/url_graph_visitor.js +14 -12
- package/src/kitchen/url_graph/url_info_transformations.js +180 -184
- package/src/plugins/autoreload/client/autoreload.js +1 -0
- package/src/plugins/autoreload/client/reload.js +6 -6
- package/src/plugins/autoreload/jsenv_plugin_autoreload.js +2 -2
- package/src/plugins/autoreload/jsenv_plugin_autoreload_client.js +2 -2
- package/src/plugins/autoreload/jsenv_plugin_autoreload_server.js +84 -78
- package/src/plugins/autoreload/jsenv_plugin_hot_search_param.js +52 -0
- package/src/plugins/cache_control/jsenv_plugin_cache_control.js +1 -1
- package/src/plugins/commonjs_globals/jsenv_plugin_commonjs_globals.js +2 -2
- package/src/plugins/global_scenarios/jsenv_plugin_global_scenarios.js +3 -3
- package/src/plugins/import_meta_hot/client/import_meta_hot.js +4 -4
- package/src/plugins/import_meta_hot/jsenv_plugin_import_meta_hot.js +18 -20
- package/src/plugins/import_meta_scenarios/jsenv_plugin_import_meta_scenarios.js +2 -2
- package/src/plugins/importmap/jsenv_plugin_importmap.js +35 -37
- package/src/plugins/inlining/jsenv_plugin_inlining.js +1 -17
- package/src/plugins/inlining/jsenv_plugin_inlining_as_data_url.js +70 -50
- package/src/plugins/inlining/jsenv_plugin_inlining_into_html.js +72 -54
- package/src/plugins/plugin_controller.js +92 -27
- package/src/plugins/protocol_file/jsenv_plugin_protocol_file.js +18 -20
- package/src/plugins/reference_analysis/css/jsenv_plugin_css_reference_analysis.js +4 -5
- package/src/plugins/reference_analysis/data_urls/jsenv_plugin_data_urls_analysis.js +18 -16
- package/src/plugins/reference_analysis/directory/jsenv_plugin_directory_reference_analysis.js +13 -20
- package/src/plugins/reference_analysis/html/jsenv_plugin_html_reference_analysis.js +55 -72
- package/src/plugins/reference_analysis/js/jsenv_plugin_js_reference_analysis.js +33 -42
- package/src/plugins/reference_analysis/jsenv_plugin_reference_analysis.js +16 -7
- package/src/plugins/reference_analysis/webmanifest/jsenv_plugin_webmanifest_reference_analysis.js +4 -3
- package/src/plugins/resolution_node_esm/jsenv_plugin_node_esm_resolution.js +16 -6
- package/src/plugins/resolution_node_esm/node_esm_resolver.js +30 -24
- package/src/plugins/resolution_web/jsenv_plugin_web_resolution.js +8 -5
- package/src/plugins/ribbon/jsenv_plugin_ribbon.js +3 -3
- package/src/plugins/server_events/client/server_events_client.js +460 -15
- package/src/plugins/server_events/jsenv_plugin_server_events_client_injection.js +13 -29
- package/src/plugins/version_search_param/jsenv_plugin_version_search_param.js +1 -1
- package/src/build/version_generator.js +0 -19
- package/src/kitchen/url_graph/url_graph_loader.js +0 -77
- package/src/kitchen/url_graph.js +0 -322
- package/src/plugins/autoreload/jsenv_plugin_hmr.js +0 -42
- package/src/plugins/resolution_node_esm/url_type_from_reference.js +0 -13
- package/src/plugins/server_events/client/connection_manager.js +0 -170
- package/src/plugins/server_events/client/event_source_connection.js +0 -83
- package/src/plugins/server_events/client/events_manager.js +0 -75
- package/src/plugins/server_events/client/web_socket_connection.js +0 -81
- /package/src/kitchen/{url_specifier_encoding.js → url_graph/url_specifier_encoding.js} +0 -0
|
@@ -1,58 +1,78 @@
|
|
|
1
|
-
import { DATA_URL } from "@jsenv/urls";
|
|
1
|
+
import { DATA_URL, injectQueryParamsIntoSpecifier } from "@jsenv/urls";
|
|
2
2
|
|
|
3
3
|
export const jsenvPluginInliningAsDataUrl = () => {
|
|
4
4
|
return {
|
|
5
5
|
name: "jsenv:inlining_as_data_url",
|
|
6
6
|
appliesDuring: "*",
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
7
|
+
// if the referenced url is a worker we could use
|
|
8
|
+
// https://www.oreilly.com/library/view/web-workers/9781449322120/ch04.html
|
|
9
|
+
// but maybe we should rather use ?object_url
|
|
10
|
+
// or people could do this:
|
|
11
|
+
// import workerText from './worker.js?text'
|
|
12
|
+
// const blob = new Blob(workerText, { type: 'text/javascript' })
|
|
13
|
+
// window.URL.createObjectURL(blob)
|
|
14
|
+
// in any case the recommended way is to use an url
|
|
15
|
+
// to benefit from shared worker and reuse worker between tabs
|
|
16
|
+
formatReference: (reference) => {
|
|
17
|
+
if (!reference.searchParams.has("inline")) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
if (reference.type === "sourcemap_comment") {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
// <link rel="stylesheet"> and <script> can be inlined in the html
|
|
24
|
+
if (
|
|
25
|
+
reference.type === "link_href" &&
|
|
26
|
+
reference.subtype === "stylesheet"
|
|
27
|
+
) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
if (
|
|
31
|
+
reference.original &&
|
|
32
|
+
reference.original.type === "link_href" &&
|
|
33
|
+
reference.original.subtype === "stylesheet"
|
|
34
|
+
) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
if (reference.type === "script") {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const specifierWithBase64Param = injectQueryParamsIntoSpecifier(
|
|
41
|
+
reference.specifier,
|
|
42
|
+
{ as_base_64: "", inline: undefined },
|
|
43
|
+
);
|
|
44
|
+
const referenceInlined = reference.inline({
|
|
45
|
+
line: reference.line,
|
|
46
|
+
column: reference.column,
|
|
47
|
+
isOriginal: reference.isOriginal,
|
|
48
|
+
specifier: specifierWithBase64Param,
|
|
49
|
+
});
|
|
50
|
+
const urlInfoInlined = referenceInlined.urlInfo;
|
|
51
|
+
return (async () => {
|
|
52
|
+
await urlInfoInlined.cook();
|
|
53
|
+
const base64Url = DATA_URL.stringify({
|
|
54
|
+
mediaType: urlInfoInlined.contentType,
|
|
55
|
+
base64Flag: true,
|
|
56
|
+
data: urlInfoInlined.content,
|
|
57
|
+
});
|
|
58
|
+
return base64Url;
|
|
59
|
+
})();
|
|
60
|
+
},
|
|
61
|
+
fetchUrlContent: async (urlInfo) => {
|
|
62
|
+
const withoutBase64ParamUrlInfo =
|
|
63
|
+
urlInfo.getWithoutSearchParam("as_base_64");
|
|
64
|
+
if (!withoutBase64ParamUrlInfo) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
await withoutBase64ParamUrlInfo.cook();
|
|
68
|
+
const contentAsBase64 = Buffer.from(
|
|
69
|
+
withoutBase64ParamUrlInfo.content,
|
|
70
|
+
).toString("base64");
|
|
71
|
+
return {
|
|
72
|
+
originalContent: withoutBase64ParamUrlInfo.originalContent,
|
|
73
|
+
content: contentAsBase64,
|
|
74
|
+
contentType: withoutBase64ParamUrlInfo.contentType,
|
|
75
|
+
};
|
|
56
76
|
},
|
|
57
77
|
};
|
|
58
78
|
};
|
|
@@ -9,46 +9,57 @@ import {
|
|
|
9
9
|
setHtmlNodeText,
|
|
10
10
|
getHtmlNodePosition,
|
|
11
11
|
} from "@jsenv/ast";
|
|
12
|
+
import { generateInlineContentUrl, urlToExtension } from "@jsenv/urls";
|
|
12
13
|
|
|
13
14
|
export const jsenvPluginInliningIntoHtml = () => {
|
|
14
15
|
return {
|
|
15
16
|
name: "jsenv:inlining_into_html",
|
|
16
17
|
appliesDuring: "*",
|
|
17
18
|
transformUrlContent: {
|
|
18
|
-
html: async (urlInfo
|
|
19
|
+
html: async (urlInfo) => {
|
|
19
20
|
const htmlAst = parseHtmlString(urlInfo.content);
|
|
20
21
|
const mutations = [];
|
|
21
22
|
const actions = [];
|
|
22
23
|
|
|
23
24
|
const onStyleSheet = (linkNode, { href }) => {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
25
|
+
let linkReference = null;
|
|
26
|
+
for (const referenceToOther of urlInfo.referenceToOthersSet) {
|
|
27
|
+
if (
|
|
28
|
+
referenceToOther.generatedSpecifier === href &&
|
|
29
|
+
referenceToOther.type === "link_href" &&
|
|
30
|
+
referenceToOther.subtype === "stylesheet"
|
|
31
|
+
) {
|
|
32
|
+
linkReference = referenceToOther;
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (!linkReference.searchParams.has("inline")) {
|
|
34
37
|
return;
|
|
35
38
|
}
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
await context.cook(linkUrlInfo, {
|
|
39
|
-
reference: linkReference,
|
|
40
|
-
});
|
|
41
|
-
const { line, column, isOriginal } = getHtmlNodePosition(linkNode, {
|
|
39
|
+
const { line, column, lineEnd, columnEnd, isOriginal } =
|
|
40
|
+
getHtmlNodePosition(linkNode, {
|
|
42
41
|
preferOriginal: true,
|
|
43
42
|
});
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
43
|
+
const linkInlineUrl = generateInlineContentUrl({
|
|
44
|
+
url: linkReference.url,
|
|
45
|
+
extension: urlToExtension(linkReference.url),
|
|
46
|
+
line,
|
|
47
|
+
column,
|
|
48
|
+
lineEnd,
|
|
49
|
+
columnEnd,
|
|
50
|
+
});
|
|
51
|
+
const linkReferenceInlined = linkReference.inline({
|
|
52
|
+
line: line - 1,
|
|
53
|
+
column,
|
|
54
|
+
isOriginal,
|
|
55
|
+
specifier: linkInlineUrl,
|
|
56
|
+
type: "style",
|
|
57
|
+
expectedType: linkReference.expectedType,
|
|
58
|
+
});
|
|
59
|
+
const linkUrlInfoInlined = linkReferenceInlined.urlInfo;
|
|
60
|
+
|
|
61
|
+
actions.push(async () => {
|
|
62
|
+
await linkUrlInfoInlined.cook();
|
|
52
63
|
mutations.push(() => {
|
|
53
64
|
setHtmlNodeAttributes(linkNode, {
|
|
54
65
|
"inlined-from-href": href,
|
|
@@ -62,43 +73,50 @@ export const jsenvPluginInliningIntoHtml = () => {
|
|
|
62
73
|
});
|
|
63
74
|
linkNode.nodeName = "style";
|
|
64
75
|
linkNode.tagName = "style";
|
|
65
|
-
setHtmlNodeText(linkNode,
|
|
76
|
+
setHtmlNodeText(linkNode, linkUrlInfoInlined.content, {
|
|
66
77
|
indentation: "auto",
|
|
67
78
|
});
|
|
68
79
|
});
|
|
69
80
|
});
|
|
70
81
|
};
|
|
71
82
|
const onScriptWithSrc = (scriptNode, { src }) => {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
83
|
+
let scriptReference;
|
|
84
|
+
for (const dependencyReference of urlInfo.referenceToOthersSet) {
|
|
85
|
+
if (
|
|
86
|
+
dependencyReference.generatedSpecifier === src &&
|
|
87
|
+
dependencyReference.type === "script"
|
|
88
|
+
) {
|
|
89
|
+
scriptReference = dependencyReference;
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (!scriptReference.searchParams.has("inline")) {
|
|
79
94
|
return;
|
|
80
95
|
}
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
actions.push(async () => {
|
|
85
|
-
await context.cook(scriptUrlInfo, {
|
|
86
|
-
reference: scriptReference,
|
|
87
|
-
});
|
|
88
|
-
const { line, column, isOriginal } = getHtmlNodePosition(
|
|
89
|
-
scriptNode,
|
|
90
|
-
{
|
|
91
|
-
preferOriginal: true,
|
|
92
|
-
},
|
|
93
|
-
);
|
|
94
|
-
context.referenceUtils.becomesInline(scriptReference, {
|
|
95
|
-
line: line - 1,
|
|
96
|
-
column,
|
|
97
|
-
isOriginal,
|
|
98
|
-
specifier: scriptReference.generatedSpecifier,
|
|
99
|
-
content: scriptUrlInfo.content,
|
|
100
|
-
contentType: scriptUrlInfo.contentType,
|
|
96
|
+
const { line, column, lineEnd, columnEnd, isOriginal } =
|
|
97
|
+
getHtmlNodePosition(scriptNode, {
|
|
98
|
+
preferOriginal: true,
|
|
101
99
|
});
|
|
100
|
+
const scriptInlineUrl = generateInlineContentUrl({
|
|
101
|
+
url: scriptReference.url,
|
|
102
|
+
extension: urlToExtension(scriptReference.url),
|
|
103
|
+
line,
|
|
104
|
+
column,
|
|
105
|
+
lineEnd,
|
|
106
|
+
columnEnd,
|
|
107
|
+
});
|
|
108
|
+
const scriptReferenceInlined = scriptReference.inline({
|
|
109
|
+
line: line - 1,
|
|
110
|
+
column,
|
|
111
|
+
isOriginal,
|
|
112
|
+
specifier: scriptInlineUrl,
|
|
113
|
+
type: scriptReference.type,
|
|
114
|
+
subtype: scriptReference.subtype,
|
|
115
|
+
expectedType: scriptReference.expectedType,
|
|
116
|
+
});
|
|
117
|
+
const scriptUrlInfoInlined = scriptReferenceInlined.urlInfo;
|
|
118
|
+
actions.push(async () => {
|
|
119
|
+
await scriptUrlInfoInlined.cook();
|
|
102
120
|
mutations.push(() => {
|
|
103
121
|
setHtmlNodeAttributes(scriptNode, {
|
|
104
122
|
"inlined-from-src": src,
|
|
@@ -107,7 +125,7 @@ export const jsenvPluginInliningIntoHtml = () => {
|
|
|
107
125
|
"integrity": undefined,
|
|
108
126
|
"jsenv-inlined-by": "jsenv:inlining_into_html",
|
|
109
127
|
});
|
|
110
|
-
setHtmlNodeText(scriptNode,
|
|
128
|
+
setHtmlNodeText(scriptNode, scriptUrlInfoInlined.content, {
|
|
111
129
|
indentation: "auto",
|
|
112
130
|
});
|
|
113
131
|
});
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { performance } from "node:perf_hooks";
|
|
2
|
+
import {
|
|
3
|
+
parseHtmlString,
|
|
4
|
+
stringifyHtmlAst,
|
|
5
|
+
injectHtmlNodeAsEarlyAsPossible,
|
|
6
|
+
createHtmlNode,
|
|
7
|
+
} from "@jsenv/ast";
|
|
2
8
|
|
|
3
9
|
const HOOK_NAMES = [
|
|
4
10
|
"init",
|
|
@@ -134,7 +140,7 @@ export const createPluginController = (kitchenContext) => {
|
|
|
134
140
|
let lastPluginUsed = null;
|
|
135
141
|
let currentPlugin = null;
|
|
136
142
|
let currentHookName = null;
|
|
137
|
-
const callHook = (hook, info
|
|
143
|
+
const callHook = (hook, info) => {
|
|
138
144
|
const hookFn = getHookFunction(hook, info);
|
|
139
145
|
if (!hookFn) {
|
|
140
146
|
return null;
|
|
@@ -146,21 +152,17 @@ export const createPluginController = (kitchenContext) => {
|
|
|
146
152
|
lastPluginUsed = hook.plugin;
|
|
147
153
|
currentPlugin = hook.plugin;
|
|
148
154
|
currentHookName = hook.name;
|
|
149
|
-
let valueReturned = hookFn(info
|
|
155
|
+
let valueReturned = hookFn(info);
|
|
150
156
|
currentPlugin = null;
|
|
151
157
|
currentHookName = null;
|
|
152
158
|
if (info.timing) {
|
|
153
159
|
info.timing[`${hook.name}-${hook.plugin.name.replace("jsenv:", "")}`] =
|
|
154
160
|
performance.now() - startTimestamp;
|
|
155
161
|
}
|
|
156
|
-
valueReturned = assertAndNormalizeReturnValue(
|
|
157
|
-
hook.name,
|
|
158
|
-
valueReturned,
|
|
159
|
-
info,
|
|
160
|
-
);
|
|
162
|
+
valueReturned = assertAndNormalizeReturnValue(hook, valueReturned, info);
|
|
161
163
|
return valueReturned;
|
|
162
164
|
};
|
|
163
|
-
const callAsyncHook = async (hook, info
|
|
165
|
+
const callAsyncHook = async (hook, info) => {
|
|
164
166
|
const hookFn = getHookFunction(hook, info);
|
|
165
167
|
if (!hookFn) {
|
|
166
168
|
return null;
|
|
@@ -173,38 +175,37 @@ export const createPluginController = (kitchenContext) => {
|
|
|
173
175
|
lastPluginUsed = hook.plugin;
|
|
174
176
|
currentPlugin = hook.plugin;
|
|
175
177
|
currentHookName = hook.name;
|
|
176
|
-
let valueReturned = await hookFn(info
|
|
178
|
+
let valueReturned = await hookFn(info);
|
|
177
179
|
currentPlugin = null;
|
|
178
180
|
currentHookName = null;
|
|
179
181
|
if (info.timing) {
|
|
180
182
|
info.timing[`${hook.name}-${hook.plugin.name.replace("jsenv:", "")}`] =
|
|
181
183
|
performance.now() - startTimestamp;
|
|
182
184
|
}
|
|
183
|
-
valueReturned = assertAndNormalizeReturnValue(
|
|
184
|
-
hook.name,
|
|
185
|
-
valueReturned,
|
|
186
|
-
info,
|
|
187
|
-
);
|
|
185
|
+
valueReturned = assertAndNormalizeReturnValue(hook, valueReturned, info);
|
|
188
186
|
return valueReturned;
|
|
189
187
|
};
|
|
190
188
|
|
|
191
|
-
const callHooks = (hookName, info,
|
|
189
|
+
const callHooks = (hookName, info, callback) => {
|
|
192
190
|
const hooks = hookGroups[hookName];
|
|
193
191
|
if (hooks) {
|
|
192
|
+
const setHookParams = (firstArg = info) => {
|
|
193
|
+
info = firstArg;
|
|
194
|
+
};
|
|
194
195
|
for (const hook of hooks) {
|
|
195
|
-
const returnValue = callHook(hook, info
|
|
196
|
+
const returnValue = callHook(hook, info);
|
|
196
197
|
if (returnValue && callback) {
|
|
197
|
-
callback(returnValue, hook.plugin);
|
|
198
|
+
callback(returnValue, hook.plugin, setHookParams);
|
|
198
199
|
}
|
|
199
200
|
}
|
|
200
201
|
}
|
|
201
202
|
};
|
|
202
|
-
const callAsyncHooks = async (hookName, info,
|
|
203
|
+
const callAsyncHooks = async (hookName, info, callback) => {
|
|
203
204
|
const hooks = hookGroups[hookName];
|
|
204
205
|
if (hooks) {
|
|
205
206
|
await hooks.reduce(async (previous, hook) => {
|
|
206
207
|
await previous;
|
|
207
|
-
const returnValue = await callAsyncHook(hook, info
|
|
208
|
+
const returnValue = await callAsyncHook(hook, info);
|
|
208
209
|
if (returnValue && callback) {
|
|
209
210
|
await callback(returnValue, hook.plugin);
|
|
210
211
|
}
|
|
@@ -212,11 +213,11 @@ export const createPluginController = (kitchenContext) => {
|
|
|
212
213
|
}
|
|
213
214
|
};
|
|
214
215
|
|
|
215
|
-
const callHooksUntil = (hookName, info
|
|
216
|
+
const callHooksUntil = (hookName, info) => {
|
|
216
217
|
const hooks = hookGroups[hookName];
|
|
217
218
|
if (hooks) {
|
|
218
219
|
for (const hook of hooks) {
|
|
219
|
-
const returnValue = callHook(hook, info
|
|
220
|
+
const returnValue = callHook(hook, info);
|
|
220
221
|
if (returnValue) {
|
|
221
222
|
return returnValue;
|
|
222
223
|
}
|
|
@@ -224,7 +225,7 @@ export const createPluginController = (kitchenContext) => {
|
|
|
224
225
|
}
|
|
225
226
|
return null;
|
|
226
227
|
};
|
|
227
|
-
const callAsyncHooksUntil = (hookName, info
|
|
228
|
+
const callAsyncHooksUntil = (hookName, info) => {
|
|
228
229
|
const hooks = hookGroups[hookName];
|
|
229
230
|
if (!hooks) {
|
|
230
231
|
return null;
|
|
@@ -238,7 +239,7 @@ export const createPluginController = (kitchenContext) => {
|
|
|
238
239
|
return resolve();
|
|
239
240
|
}
|
|
240
241
|
const hook = hooks[index];
|
|
241
|
-
const returnValue = callAsyncHook(hook, info
|
|
242
|
+
const returnValue = callAsyncHook(hook, info);
|
|
242
243
|
return Promise.resolve(returnValue).then((output) => {
|
|
243
244
|
if (output) {
|
|
244
245
|
return resolve(output);
|
|
@@ -285,16 +286,20 @@ const getHookFunction = (
|
|
|
285
286
|
return hookValue;
|
|
286
287
|
};
|
|
287
288
|
|
|
288
|
-
const assertAndNormalizeReturnValue = (
|
|
289
|
+
const assertAndNormalizeReturnValue = (hook, returnValue, info) => {
|
|
289
290
|
// all hooks are allowed to return null/undefined as a signal of "I don't do anything"
|
|
290
291
|
if (returnValue === null || returnValue === undefined) {
|
|
291
292
|
return returnValue;
|
|
292
293
|
}
|
|
293
294
|
for (const returnValueAssertion of returnValueAssertions) {
|
|
294
|
-
if (!returnValueAssertion.appliesTo.includes(
|
|
295
|
+
if (!returnValueAssertion.appliesTo.includes(hook.name)) {
|
|
295
296
|
continue;
|
|
296
297
|
}
|
|
297
|
-
const assertionResult = returnValueAssertion.assertion(
|
|
298
|
+
const assertionResult = returnValueAssertion.assertion(
|
|
299
|
+
returnValue,
|
|
300
|
+
info,
|
|
301
|
+
hook,
|
|
302
|
+
);
|
|
298
303
|
if (assertionResult !== undefined) {
|
|
299
304
|
// normalization
|
|
300
305
|
returnValue = assertionResult;
|
|
@@ -328,7 +333,7 @@ const returnValueAssertions = [
|
|
|
328
333
|
"finalizeUrlContent",
|
|
329
334
|
"optimizeUrlContent",
|
|
330
335
|
],
|
|
331
|
-
assertion: (valueReturned, urlInfo) => {
|
|
336
|
+
assertion: (valueReturned, urlInfo, hook) => {
|
|
332
337
|
if (typeof valueReturned === "string" || Buffer.isBuffer(valueReturned)) {
|
|
333
338
|
return { content: valueReturned };
|
|
334
339
|
}
|
|
@@ -337,6 +342,12 @@ const returnValueAssertions = [
|
|
|
337
342
|
if (urlInfo.url.startsWith("ignore:")) {
|
|
338
343
|
return undefined;
|
|
339
344
|
}
|
|
345
|
+
if (urlInfo.type === "html") {
|
|
346
|
+
const { scriptInjections } = valueReturned;
|
|
347
|
+
if (scriptInjections) {
|
|
348
|
+
return applyScriptInjections(urlInfo, scriptInjections, hook);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
340
351
|
if (typeof content !== "string" && !Buffer.isBuffer(content) && !body) {
|
|
341
352
|
throw new Error(
|
|
342
353
|
`Unexpected "content" returned by plugin: it must be a string or a buffer; got ${content}`,
|
|
@@ -350,3 +361,57 @@ const returnValueAssertions = [
|
|
|
350
361
|
},
|
|
351
362
|
},
|
|
352
363
|
];
|
|
364
|
+
|
|
365
|
+
const applyScriptInjections = (htmlUrlInfo, scriptInjections, hook) => {
|
|
366
|
+
const htmlAst = parseHtmlString(htmlUrlInfo.content);
|
|
367
|
+
|
|
368
|
+
scriptInjections.reverse().forEach((scriptInjection) => {
|
|
369
|
+
const { setup } = scriptInjection;
|
|
370
|
+
if (setup) {
|
|
371
|
+
const setupGlobalName = setup.name;
|
|
372
|
+
const setupParamSource = stringifyParams(setup.param, " ");
|
|
373
|
+
const inlineJs = `${setupGlobalName}({${setupParamSource}})`;
|
|
374
|
+
injectHtmlNodeAsEarlyAsPossible(
|
|
375
|
+
htmlAst,
|
|
376
|
+
createHtmlNode({
|
|
377
|
+
tagName: "script",
|
|
378
|
+
textContent: inlineJs,
|
|
379
|
+
}),
|
|
380
|
+
hook.plugin.name,
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
const scriptReference = htmlUrlInfo.dependencies.inject({
|
|
384
|
+
type: "script",
|
|
385
|
+
subtype: scriptInjection.type === "module" ? "js_module" : "js_classic",
|
|
386
|
+
expectedType:
|
|
387
|
+
scriptInjection.type === "module" ? "js_module" : "js_classic",
|
|
388
|
+
specifier: scriptInjection.src,
|
|
389
|
+
});
|
|
390
|
+
injectHtmlNodeAsEarlyAsPossible(
|
|
391
|
+
htmlAst,
|
|
392
|
+
createHtmlNode({
|
|
393
|
+
tagName: "script",
|
|
394
|
+
...(scriptInjection.type === "module" ? { type: "module" } : {}),
|
|
395
|
+
src: scriptReference.generatedSpecifier,
|
|
396
|
+
}),
|
|
397
|
+
hook.plugin.name,
|
|
398
|
+
);
|
|
399
|
+
});
|
|
400
|
+
const htmlModified = stringifyHtmlAst(htmlAst);
|
|
401
|
+
return {
|
|
402
|
+
content: htmlModified,
|
|
403
|
+
};
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
const stringifyParams = (params, prefix = "") => {
|
|
407
|
+
const source = JSON.stringify(params, null, prefix);
|
|
408
|
+
if (prefix.length) {
|
|
409
|
+
// remove leading "{\n"
|
|
410
|
+
// remove leading prefix
|
|
411
|
+
// remove trailing "\n}"
|
|
412
|
+
return source.slice(2 + prefix.length, -2);
|
|
413
|
+
}
|
|
414
|
+
// remove leading "{"
|
|
415
|
+
// remove trailing "}"
|
|
416
|
+
return source.slice(1, -1);
|
|
417
|
+
};
|
|
@@ -84,7 +84,7 @@ export const jsenvPluginProtocolFile = ({
|
|
|
84
84
|
magicDirectoryIndex,
|
|
85
85
|
magicExtensions: getExtensionsToTry(
|
|
86
86
|
magicExtensions,
|
|
87
|
-
reference.
|
|
87
|
+
reference.ownerUrlInfo.url,
|
|
88
88
|
),
|
|
89
89
|
});
|
|
90
90
|
if (filesystemResolution.stat) {
|
|
@@ -117,13 +117,12 @@ export const jsenvPluginProtocolFile = ({
|
|
|
117
117
|
name: "jsenv:fs_resolution",
|
|
118
118
|
appliesDuring: "*",
|
|
119
119
|
resolveReference: {
|
|
120
|
-
filesystem: (reference
|
|
121
|
-
const
|
|
122
|
-
const parentUrlInfo = context.urlGraph.getUrlInfo(parentUrl);
|
|
120
|
+
filesystem: (reference) => {
|
|
121
|
+
const ownerUrlInfo = reference.ownerUrlInfo;
|
|
123
122
|
const baseUrl =
|
|
124
|
-
|
|
125
|
-
? ensurePathnameTrailingSlash(
|
|
126
|
-
:
|
|
123
|
+
ownerUrlInfo && ownerUrlInfo.type === "directory"
|
|
124
|
+
? ensurePathnameTrailingSlash(ownerUrlInfo.url)
|
|
125
|
+
: ownerUrlInfo.url;
|
|
127
126
|
return new URL(reference.specifier, baseUrl).href;
|
|
128
127
|
},
|
|
129
128
|
},
|
|
@@ -141,14 +140,15 @@ export const jsenvPluginProtocolFile = ({
|
|
|
141
140
|
}
|
|
142
141
|
return null;
|
|
143
142
|
},
|
|
144
|
-
formatReference: (reference
|
|
143
|
+
formatReference: (reference) => {
|
|
145
144
|
if (!reference.generatedUrl.startsWith("file:")) {
|
|
146
145
|
return null;
|
|
147
146
|
}
|
|
148
|
-
|
|
147
|
+
const { rootDirectoryUrl } = reference.ownerUrlInfo.context;
|
|
148
|
+
if (urlIsInsideOf(reference.generatedUrl, rootDirectoryUrl)) {
|
|
149
149
|
return `/${urlToRelativeUrl(
|
|
150
150
|
reference.generatedUrl,
|
|
151
|
-
|
|
151
|
+
rootDirectoryUrl,
|
|
152
152
|
)}`;
|
|
153
153
|
}
|
|
154
154
|
return `/@fs/${reference.generatedUrl.slice("file:///".length)}`;
|
|
@@ -157,19 +157,16 @@ export const jsenvPluginProtocolFile = ({
|
|
|
157
157
|
{
|
|
158
158
|
name: "jsenv:file_url_fetching",
|
|
159
159
|
appliesDuring: "*",
|
|
160
|
-
fetchUrlContent: (urlInfo
|
|
160
|
+
fetchUrlContent: (urlInfo) => {
|
|
161
161
|
if (!urlInfo.url.startsWith("file:")) {
|
|
162
162
|
return null;
|
|
163
163
|
}
|
|
164
164
|
const urlObject = new URL(urlInfo.url);
|
|
165
|
-
if (
|
|
165
|
+
if (urlInfo.firstReference.leadsToADirectory) {
|
|
166
166
|
const directoryEntries = readdirSync(urlObject);
|
|
167
167
|
let filename;
|
|
168
|
-
if (
|
|
169
|
-
|
|
170
|
-
context.reference.parentUrl,
|
|
171
|
-
);
|
|
172
|
-
filename = `${parentUrlInfo.filename}${context.reference.specifier}/`;
|
|
168
|
+
if (urlInfo.firstReference.type === "filesystem") {
|
|
169
|
+
filename = `${urlInfo.firstReference.ownerUrlInfo.filename}${urlInfo.firstReference.specifier}/`;
|
|
173
170
|
} else {
|
|
174
171
|
filename = `${urlToFilename(urlInfo.url)}/`;
|
|
175
172
|
}
|
|
@@ -182,10 +179,11 @@ export const jsenvPluginProtocolFile = ({
|
|
|
182
179
|
}
|
|
183
180
|
const fileBuffer = readFileSync(urlObject);
|
|
184
181
|
const contentType = CONTENT_TYPE.fromUrlExtension(urlInfo.url);
|
|
182
|
+
const content = CONTENT_TYPE.isTextual(contentType)
|
|
183
|
+
? String(fileBuffer)
|
|
184
|
+
: fileBuffer;
|
|
185
185
|
return {
|
|
186
|
-
content
|
|
187
|
-
? String(fileBuffer)
|
|
188
|
-
: fileBuffer,
|
|
186
|
+
content,
|
|
189
187
|
contentType,
|
|
190
188
|
};
|
|
191
189
|
},
|
|
@@ -15,7 +15,7 @@ export const jsenvPluginCssReferenceAnalysis = () => {
|
|
|
15
15
|
};
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
-
const parseAndTransformCssUrls = async (urlInfo
|
|
18
|
+
const parseAndTransformCssUrls = async (urlInfo) => {
|
|
19
19
|
const cssUrls = await parseCssUrls({
|
|
20
20
|
css: urlInfo.content,
|
|
21
21
|
url: urlInfo.originalUrl,
|
|
@@ -23,7 +23,7 @@ const parseAndTransformCssUrls = async (urlInfo, context) => {
|
|
|
23
23
|
const actions = [];
|
|
24
24
|
const magicSource = createMagicSource(urlInfo.content);
|
|
25
25
|
for (const cssUrl of cssUrls) {
|
|
26
|
-
const
|
|
26
|
+
const reference = urlInfo.dependencies.found({
|
|
27
27
|
type: cssUrl.type,
|
|
28
28
|
specifier: cssUrl.specifier,
|
|
29
29
|
specifierStart: cssUrl.start,
|
|
@@ -32,9 +32,8 @@ const parseAndTransformCssUrls = async (urlInfo, context) => {
|
|
|
32
32
|
specifierColumn: cssUrl.column,
|
|
33
33
|
});
|
|
34
34
|
actions.push(async () => {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
);
|
|
35
|
+
await reference.readGeneratedSpecifier();
|
|
36
|
+
const replacement = reference.generatedSpecifier;
|
|
38
37
|
magicSource.replace({
|
|
39
38
|
start: cssUrl.start,
|
|
40
39
|
end: cssUrl.end,
|