@jsenv/core 29.6.0 → 29.7.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/{readme.md → README.md} +2 -28
- package/dist/main.js +151 -95
- package/package.json +4 -4
- package/src/dev/start_dev_server.js +8 -0
- package/src/main.js +2 -2
- package/src/plugins/inject_globals/jsenv_plugin_inject_globals.js +20 -8
- package/src/plugins/inline/jsenv_plugin_html_inline_content.js +2 -0
- package/src/plugins/minification/jsenv_plugin_minification.js +27 -24
- package/src/plugins/placeholders/jsenv_plugin_placeholders.js +36 -0
- package/src/{helpers → plugins/placeholders}/replace_placeholders.js +10 -2
- package/src/plugins/ribbon/jsenv_plugin_ribbon.js +1 -1
- package/src/plugins/supervisor/jsenv_plugin_supervisor.js +2 -1
- package/src/plugins/url_analysis/html/html_urls.js +4 -5
- package/src/plugins/url_version/jsenv_plugin_url_version.js +1 -1
package/{readme.md → README.md}
RENAMED
|
@@ -7,26 +7,9 @@ It has naturally evolved to cover the core needs of a JavaScript project: develo
|
|
|
7
7
|
- :sparkles: Dev, tests and build in a single tool
|
|
8
8
|
- :ok_hand: Seamless integration with standard HTML, CSS and JS
|
|
9
9
|
|
|
10
|
-
#
|
|
11
|
-
|
|
12
|
-
The following command can be used to create a jsenv project on a machine.
|
|
13
|
-
|
|
14
|
-
```console
|
|
15
|
-
npm create jsenv@latest
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
This command prompts to choose a demo from a list.
|
|
19
|
-
Each demo contains preconfigured scripts:
|
|
20
|
-
|
|
21
|
-
- `npm run dev`: starts a dev server with autoreload.
|
|
22
|
-
- `npm run test`: execute test files on browsers(s) and/or Node.js.
|
|
23
|
-
- `npm run build`: generate files optimized for production.
|
|
24
|
-
|
|
25
|
-
> **Info**
|
|
26
|
-
> Executing "npm install" in web demos can take time.
|
|
27
|
-
> It is because in these demos tests are runned in headless browsers that needs to be installed first.
|
|
10
|
+
# Documentation
|
|
28
11
|
|
|
29
|
-
|
|
12
|
+
https://github.com/jsenv/jsenv-core/wiki
|
|
30
13
|
|
|
31
14
|
# Installation
|
|
32
15
|
|
|
@@ -36,15 +19,6 @@ npm install --save-dev @jsenv/core
|
|
|
36
19
|
|
|
37
20
|
_@jsenv/core_ is tested on Mac, Windows, Linux with Node.js 18.5.0. Other operating systems and Node.js versions are not tested.
|
|
38
21
|
|
|
39
|
-
# Documentation
|
|
40
|
-
|
|
41
|
-
| Link | Description |
|
|
42
|
-
| -------------------------------------------------- | ---------------------------------------------- |
|
|
43
|
-
| [Browser support](./docs/browser_support.md) | Documentation around browser support |
|
|
44
|
-
| [Assets and workers](./docs/assets_and_workers.md) | How to reference files within a file |
|
|
45
|
-
| [Import resolution](./docs/import_resolution.md) | Import resolution inside js modules |
|
|
46
|
-
| [NPM package](./docs/npm_package.md) | How to use a NPM package (especially commonjs) |
|
|
47
|
-
|
|
48
22
|
# Name
|
|
49
23
|
|
|
50
24
|
The name "jsenv" stands for JavaScript environments. This is because the original purpose of jsenv was to bring closer two JavaScript runtimes: web browsers and Node.js.
|
package/dist/main.js
CHANGED
|
@@ -337,9 +337,12 @@ const pathnameToExtension$1 = pathname => {
|
|
|
337
337
|
};
|
|
338
338
|
|
|
339
339
|
const asUrlWithoutSearch = url => {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
340
|
+
if (url.includes("?")) {
|
|
341
|
+
const urlObject = new URL(url);
|
|
342
|
+
urlObject.search = "";
|
|
343
|
+
return urlObject.href;
|
|
344
|
+
}
|
|
345
|
+
return url;
|
|
343
346
|
};
|
|
344
347
|
|
|
345
348
|
// normalize url search params:
|
|
@@ -11645,9 +11648,8 @@ const visitHtmlUrls = ({
|
|
|
11645
11648
|
attributeName,
|
|
11646
11649
|
specifier
|
|
11647
11650
|
}) => {
|
|
11648
|
-
const isContentCooked = getHtmlNodeAttribute(node, "jsenv-plugin-action") === "content_cooked";
|
|
11649
11651
|
let position;
|
|
11650
|
-
if (
|
|
11652
|
+
if (getHtmlNodeAttribute(node, "jsenv-cooked-by")) {
|
|
11651
11653
|
// when generated from inline content,
|
|
11652
11654
|
// line, column is not "src" nor "inlined-from-src" but "original-position"
|
|
11653
11655
|
position = getHtmlNodePosition(node);
|
|
@@ -11682,8 +11684,7 @@ const visitHtmlUrls = ({
|
|
|
11682
11684
|
}) => {
|
|
11683
11685
|
const value = getHtmlNodeAttribute(node, attributeName);
|
|
11684
11686
|
if (value) {
|
|
11685
|
-
|
|
11686
|
-
if (jsenvInlinedBy === "jsenv:importmap") {
|
|
11687
|
+
if (getHtmlNodeAttribute(node, "jsenv-inlined-by") === "jsenv:importmap") {
|
|
11687
11688
|
// during build the importmap is inlined
|
|
11688
11689
|
// and shoud not be considered as a dependency anymore
|
|
11689
11690
|
return null;
|
|
@@ -12148,7 +12149,7 @@ const jsenvPluginHtmlInlineContent = ({
|
|
|
12148
12149
|
if (!analyzeConvertedScripts && getHtmlNodeAttribute(scriptNode, "jsenv-injected-by") === "jsenv:as_js_classic_html") {
|
|
12149
12150
|
return;
|
|
12150
12151
|
}
|
|
12151
|
-
if (getHtmlNodeAttribute(scriptNode, "jsenv-cooked-by") === "jsenv:supervisor" || getHtmlNodeAttribute(scriptNode, "jsenv-injected-by") === "jsenv:supervisor") {
|
|
12152
|
+
if (getHtmlNodeAttribute(scriptNode, "jsenv-cooked-by") === "jsenv:supervisor" || getHtmlNodeAttribute(scriptNode, "jsenv-inlined-by") === "jsenv:supervisor" || getHtmlNodeAttribute(scriptNode, "jsenv-injected-by") === "jsenv:supervisor") {
|
|
12152
12153
|
return;
|
|
12153
12154
|
}
|
|
12154
12155
|
const {
|
|
@@ -19275,7 +19276,7 @@ const jsenvPluginUrlResolution = ({
|
|
|
19275
19276
|
const jsenvPluginUrlVersion = () => {
|
|
19276
19277
|
return {
|
|
19277
19278
|
name: "jsenv:url_version",
|
|
19278
|
-
appliesDuring: "
|
|
19279
|
+
appliesDuring: "dev",
|
|
19279
19280
|
redirectUrl: reference => {
|
|
19280
19281
|
// "v" search param goal is to enable long-term cache
|
|
19281
19282
|
// for server response headers
|
|
@@ -19743,7 +19744,9 @@ const jsenvPluginSupervisor = ({
|
|
|
19743
19744
|
if (type !== "js_classic" && type !== "js_module") {
|
|
19744
19745
|
return;
|
|
19745
19746
|
}
|
|
19746
|
-
if (getHtmlNodeAttribute(node, "jsenv-cooked-by") || getHtmlNodeAttribute(node, "jsenv-inlined-by") || getHtmlNodeAttribute(node, "jsenv-injected-by"))
|
|
19747
|
+
if (getHtmlNodeAttribute(node, "jsenv-cooked-by") || getHtmlNodeAttribute(node, "jsenv-inlined-by") || getHtmlNodeAttribute(node, "jsenv-injected-by")) {
|
|
19748
|
+
return;
|
|
19749
|
+
}
|
|
19747
19750
|
const noSupervisor = getHtmlNodeAttribute(node, "no-supervisor");
|
|
19748
19751
|
if (noSupervisor !== undefined) {
|
|
19749
19752
|
return;
|
|
@@ -21708,27 +21711,30 @@ const jsenvPluginMinification = minification => {
|
|
|
21708
21711
|
context,
|
|
21709
21712
|
options: minification.json
|
|
21710
21713
|
}) : null;
|
|
21714
|
+
const cssOptimizer = minification.css ? (urlInfo, context) => minifyCss({
|
|
21715
|
+
cssUrlInfo: urlInfo,
|
|
21716
|
+
context,
|
|
21717
|
+
options: minification.css
|
|
21718
|
+
}) : null;
|
|
21719
|
+
const jsClassicOptimizer = minification.js_classic ? (urlInfo, context) => minifyJs({
|
|
21720
|
+
jsUrlInfo: urlInfo,
|
|
21721
|
+
context,
|
|
21722
|
+
options: minification.js_classic
|
|
21723
|
+
}) : null;
|
|
21724
|
+
const jsModuleOptimizer = minification.js_module ? (urlInfo, context) => minifyJs({
|
|
21725
|
+
jsUrlInfo: urlInfo,
|
|
21726
|
+
context,
|
|
21727
|
+
options: minification.js_module
|
|
21728
|
+
}) : null;
|
|
21711
21729
|
return {
|
|
21712
21730
|
name: "jsenv:minification",
|
|
21713
21731
|
appliesDuring: "build",
|
|
21714
21732
|
optimizeUrlContent: {
|
|
21715
21733
|
html: htmlOptimizer,
|
|
21716
21734
|
svg: htmlOptimizer,
|
|
21717
|
-
css:
|
|
21718
|
-
|
|
21719
|
-
|
|
21720
|
-
options: minification.css
|
|
21721
|
-
}) : null,
|
|
21722
|
-
js_classic: minification.js_classic ? (urlInfo, context) => minifyJs({
|
|
21723
|
-
jsUrlInfo: urlInfo,
|
|
21724
|
-
context,
|
|
21725
|
-
options: minification.js_classic
|
|
21726
|
-
}) : null,
|
|
21727
|
-
js_module: minification.js_module ? (urlInfo, context) => minifyJs({
|
|
21728
|
-
jsUrlInfo: urlInfo,
|
|
21729
|
-
context,
|
|
21730
|
-
options: minification.js_module
|
|
21731
|
-
}) : null,
|
|
21735
|
+
css: cssOptimizer,
|
|
21736
|
+
js_classic: jsClassicOptimizer,
|
|
21737
|
+
js_module: jsModuleOptimizer,
|
|
21732
21738
|
json: jsonOptimizer,
|
|
21733
21739
|
importmap: jsonOptimizer,
|
|
21734
21740
|
webmanifest: jsonOptimizer
|
|
@@ -22460,7 +22466,7 @@ const jsenvPluginExplorer = ({
|
|
|
22460
22466
|
|
|
22461
22467
|
const jsenvPluginRibbon = ({
|
|
22462
22468
|
rootDirectoryUrl,
|
|
22463
|
-
htmlInclude = "
|
|
22469
|
+
htmlInclude = "/**/*.html"
|
|
22464
22470
|
}) => {
|
|
22465
22471
|
const ribbonClientFileUrl = new URL("./js/ribbon.js", import.meta.url);
|
|
22466
22472
|
const associations = URL_META.resolveAssociations({
|
|
@@ -24801,6 +24807,14 @@ const inferParentFromRequest = (request, rootDirectoryUrl) => {
|
|
|
24801
24807
|
});
|
|
24802
24808
|
};
|
|
24803
24809
|
|
|
24810
|
+
/**
|
|
24811
|
+
* Start a server for source files:
|
|
24812
|
+
* - cook source files according to jsenv plugins
|
|
24813
|
+
* - inject code to autoreload the browser when a file is modified
|
|
24814
|
+
* @param {Object} devServerParameters
|
|
24815
|
+
* @param {string|url} devServerParameters.rootDirectoryUrl Root directory of the project
|
|
24816
|
+
* @return {Object} A dev server object
|
|
24817
|
+
*/
|
|
24804
24818
|
const startDevServer = async ({
|
|
24805
24819
|
signal = new AbortController().signal,
|
|
24806
24820
|
handleSIGINT = true,
|
|
@@ -28394,7 +28408,83 @@ const createBuildFilesService = ({
|
|
|
28394
28408
|
};
|
|
28395
28409
|
const SECONDS_IN_30_DAYS = 60 * 60 * 24 * 30;
|
|
28396
28410
|
|
|
28397
|
-
const
|
|
28411
|
+
const injectGlobals = (urlInfo, globals) => {
|
|
28412
|
+
if (urlInfo.type === "html") {
|
|
28413
|
+
return globalInjectorOnHtml(urlInfo, globals);
|
|
28414
|
+
}
|
|
28415
|
+
if (urlInfo.type === "js_classic" || urlInfo.type === "js_module") {
|
|
28416
|
+
return globalsInjectorOnJs(urlInfo, globals);
|
|
28417
|
+
}
|
|
28418
|
+
throw new Error(`cannot inject globals into "${urlInfo.type}"`);
|
|
28419
|
+
};
|
|
28420
|
+
const globalInjectorOnHtml = async (urlInfo, globals) => {
|
|
28421
|
+
// ideally we would inject an importmap but browser support is too low
|
|
28422
|
+
// (even worse for worker/service worker)
|
|
28423
|
+
// so for now we inject code into entry points
|
|
28424
|
+
const htmlAst = parseHtmlString(urlInfo.content, {
|
|
28425
|
+
storeOriginalPositions: false
|
|
28426
|
+
});
|
|
28427
|
+
const clientCode = generateClientCodeForGlobals({
|
|
28428
|
+
globals,
|
|
28429
|
+
isWebWorker: false
|
|
28430
|
+
});
|
|
28431
|
+
injectScriptNodeAsEarlyAsPossible(htmlAst, createHtmlNode({
|
|
28432
|
+
tagName: "script",
|
|
28433
|
+
textContent: clientCode
|
|
28434
|
+
}), "jsenv:inject_globals");
|
|
28435
|
+
return stringifyHtmlAst(htmlAst);
|
|
28436
|
+
};
|
|
28437
|
+
const globalsInjectorOnJs = async (urlInfo, globals) => {
|
|
28438
|
+
const clientCode = generateClientCodeForGlobals({
|
|
28439
|
+
globals,
|
|
28440
|
+
isWebWorker: urlInfo.subtype === "worker" || urlInfo.subtype === "service_worker" || urlInfo.subtype === "shared_worker"
|
|
28441
|
+
});
|
|
28442
|
+
const magicSource = createMagicSource(urlInfo.content);
|
|
28443
|
+
magicSource.prepend(clientCode);
|
|
28444
|
+
return magicSource.toContentAndSourcemap();
|
|
28445
|
+
};
|
|
28446
|
+
const generateClientCodeForGlobals = ({
|
|
28447
|
+
isWebWorker = false,
|
|
28448
|
+
globals
|
|
28449
|
+
}) => {
|
|
28450
|
+
const globalName = isWebWorker ? "self" : "window";
|
|
28451
|
+
return `Object.assign(${globalName}, ${JSON.stringify(globals, null, " ")});`;
|
|
28452
|
+
};
|
|
28453
|
+
|
|
28454
|
+
const jsenvPluginInjectGlobals = rawAssociations => {
|
|
28455
|
+
let resolvedAssociations;
|
|
28456
|
+
return {
|
|
28457
|
+
name: "jsenv:inject_globals",
|
|
28458
|
+
appliesDuring: "*",
|
|
28459
|
+
init: context => {
|
|
28460
|
+
resolvedAssociations = URL_META.resolveAssociations({
|
|
28461
|
+
injector: rawAssociations
|
|
28462
|
+
}, context.rootDirectoryUrl);
|
|
28463
|
+
},
|
|
28464
|
+
transformUrlContent: async (urlInfo, context) => {
|
|
28465
|
+
const {
|
|
28466
|
+
injector
|
|
28467
|
+
} = URL_META.applyAssociations({
|
|
28468
|
+
url: asUrlWithoutSearch(urlInfo.url),
|
|
28469
|
+
associations: resolvedAssociations
|
|
28470
|
+
});
|
|
28471
|
+
if (!injector) {
|
|
28472
|
+
return null;
|
|
28473
|
+
}
|
|
28474
|
+
if (typeof injector !== "function") {
|
|
28475
|
+
throw new TypeError("injector must be a function");
|
|
28476
|
+
}
|
|
28477
|
+
const globals = await injector(urlInfo, context);
|
|
28478
|
+
if (!globals || Object.keys(globals).length === 0) {
|
|
28479
|
+
return null;
|
|
28480
|
+
}
|
|
28481
|
+
return injectGlobals(urlInfo, globals);
|
|
28482
|
+
}
|
|
28483
|
+
};
|
|
28484
|
+
};
|
|
28485
|
+
|
|
28486
|
+
const replacePlaceholders = (urlInfo, replacements) => {
|
|
28487
|
+
const content = urlInfo.content;
|
|
28398
28488
|
const magicSource = createMagicSource(content);
|
|
28399
28489
|
Object.keys(replacements).forEach(key => {
|
|
28400
28490
|
let index = content.indexOf(key);
|
|
@@ -28404,7 +28494,7 @@ const replacePlaceholders = (content, replacements) => {
|
|
|
28404
28494
|
magicSource.replace({
|
|
28405
28495
|
start,
|
|
28406
28496
|
end,
|
|
28407
|
-
replacement: replacements[key]
|
|
28497
|
+
replacement: urlInfo.type === "js_classic" || urlInfo.type === "js_module" ? JSON.stringify(replacements[key], null, " ") : replacements[key]
|
|
28408
28498
|
});
|
|
28409
28499
|
index = content.indexOf(key, end);
|
|
28410
28500
|
}
|
|
@@ -28412,6 +28502,38 @@ const replacePlaceholders = (content, replacements) => {
|
|
|
28412
28502
|
return magicSource.toContentAndSourcemap();
|
|
28413
28503
|
};
|
|
28414
28504
|
|
|
28505
|
+
const jsenvPluginPlaceholders = rawAssociations => {
|
|
28506
|
+
let resolvedAssociations;
|
|
28507
|
+
return {
|
|
28508
|
+
name: "jsenv:placeholders",
|
|
28509
|
+
appliesDuring: "*",
|
|
28510
|
+
init: context => {
|
|
28511
|
+
resolvedAssociations = URL_META.resolveAssociations({
|
|
28512
|
+
replacer: rawAssociations
|
|
28513
|
+
}, context.rootDirectoryUrl);
|
|
28514
|
+
},
|
|
28515
|
+
transformUrlContent: async (urlInfo, context) => {
|
|
28516
|
+
const {
|
|
28517
|
+
replacer
|
|
28518
|
+
} = URL_META.applyAssociations({
|
|
28519
|
+
url: asUrlWithoutSearch(urlInfo.url),
|
|
28520
|
+
associations: resolvedAssociations
|
|
28521
|
+
});
|
|
28522
|
+
if (!replacer) {
|
|
28523
|
+
return null;
|
|
28524
|
+
}
|
|
28525
|
+
if (typeof replacer !== "function") {
|
|
28526
|
+
throw new TypeError("replacer must be a function");
|
|
28527
|
+
}
|
|
28528
|
+
const replacements = await replacer(urlInfo, context);
|
|
28529
|
+
if (!replacements || Object.keys(replacements).length === 0) {
|
|
28530
|
+
return null;
|
|
28531
|
+
}
|
|
28532
|
+
return replacePlaceholders(urlInfo, replacements);
|
|
28533
|
+
}
|
|
28534
|
+
};
|
|
28535
|
+
};
|
|
28536
|
+
|
|
28415
28537
|
const execute = async ({
|
|
28416
28538
|
signal = new AbortController().signal,
|
|
28417
28539
|
handleSIGINT = true,
|
|
@@ -28497,70 +28619,4 @@ const execute = async ({
|
|
|
28497
28619
|
}
|
|
28498
28620
|
};
|
|
28499
28621
|
|
|
28500
|
-
|
|
28501
|
-
if (urlInfo.type === "html") {
|
|
28502
|
-
return globalInjectorOnHtml(urlInfo, globals);
|
|
28503
|
-
}
|
|
28504
|
-
if (urlInfo.type === "js_classic" || urlInfo.type === "js_module") {
|
|
28505
|
-
return globalsInjectorOnJs(urlInfo, globals);
|
|
28506
|
-
}
|
|
28507
|
-
throw new Error(`cannot inject globals into "${urlInfo.type}"`);
|
|
28508
|
-
};
|
|
28509
|
-
const globalInjectorOnHtml = async (urlInfo, globals) => {
|
|
28510
|
-
// ideally we would inject an importmap but browser support is too low
|
|
28511
|
-
// (even worse for worker/service worker)
|
|
28512
|
-
// so for now we inject code into entry points
|
|
28513
|
-
const htmlAst = parseHtmlString(urlInfo.content, {
|
|
28514
|
-
storeOriginalPositions: false
|
|
28515
|
-
});
|
|
28516
|
-
const clientCode = generateClientCodeForGlobals({
|
|
28517
|
-
globals,
|
|
28518
|
-
isWebWorker: false
|
|
28519
|
-
});
|
|
28520
|
-
injectScriptNodeAsEarlyAsPossible(htmlAst, createHtmlNode({
|
|
28521
|
-
tagName: "script",
|
|
28522
|
-
textContent: clientCode
|
|
28523
|
-
}), "jsenv:inject_globals");
|
|
28524
|
-
return stringifyHtmlAst(htmlAst);
|
|
28525
|
-
};
|
|
28526
|
-
const globalsInjectorOnJs = async (urlInfo, globals) => {
|
|
28527
|
-
const clientCode = generateClientCodeForGlobals({
|
|
28528
|
-
globals,
|
|
28529
|
-
isWebWorker: urlInfo.subtype === "worker" || urlInfo.subtype === "service_worker" || urlInfo.subtype === "shared_worker"
|
|
28530
|
-
});
|
|
28531
|
-
const magicSource = createMagicSource(urlInfo.content);
|
|
28532
|
-
magicSource.prepend(clientCode);
|
|
28533
|
-
return magicSource.toContentAndSourcemap();
|
|
28534
|
-
};
|
|
28535
|
-
const generateClientCodeForGlobals = ({
|
|
28536
|
-
isWebWorker = false,
|
|
28537
|
-
globals
|
|
28538
|
-
}) => {
|
|
28539
|
-
const globalName = isWebWorker ? "self" : "window";
|
|
28540
|
-
return `Object.assign(${globalName}, ${JSON.stringify(globals, null, " ")});`;
|
|
28541
|
-
};
|
|
28542
|
-
|
|
28543
|
-
const jsenvPluginInjectGlobals = urlAssociations => {
|
|
28544
|
-
return {
|
|
28545
|
-
name: "jsenv:inject_globals",
|
|
28546
|
-
appliesDuring: "*",
|
|
28547
|
-
transformUrlContent: async (urlInfo, context) => {
|
|
28548
|
-
const url = Object.keys(urlAssociations).find(url => {
|
|
28549
|
-
return url === urlInfo.url;
|
|
28550
|
-
});
|
|
28551
|
-
if (!url) {
|
|
28552
|
-
return null;
|
|
28553
|
-
}
|
|
28554
|
-
let globals = urlAssociations[url];
|
|
28555
|
-
if (typeof globals === "function") {
|
|
28556
|
-
globals = await globals(urlInfo, context);
|
|
28557
|
-
}
|
|
28558
|
-
if (Object.keys(globals).length === 0) {
|
|
28559
|
-
return null;
|
|
28560
|
-
}
|
|
28561
|
-
return injectGlobals(urlInfo, globals);
|
|
28562
|
-
}
|
|
28563
|
-
};
|
|
28564
|
-
};
|
|
28565
|
-
|
|
28566
|
-
export { build, chromium, chromiumIsolatedTab, execute, executeTestPlan, firefox, firefoxIsolatedTab, jsenvPluginInjectGlobals, nodeChildProcess, nodeWorkerThread, pingServer, replacePlaceholders, startBuildServer, startDevServer, webkit, webkitIsolatedTab };
|
|
28622
|
+
export { build, chromium, chromiumIsolatedTab, execute, executeTestPlan, firefox, firefoxIsolatedTab, jsenvPluginInjectGlobals, jsenvPluginPlaceholders, nodeChildProcess, nodeWorkerThread, pingServer, startBuildServer, startDevServer, webkit, webkitIsolatedTab };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsenv/core",
|
|
3
|
-
"version": "29.
|
|
3
|
+
"version": "29.7.0",
|
|
4
4
|
"description": "Tool to develop, test and build js projects",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -69,16 +69,16 @@
|
|
|
69
69
|
"@jsenv/abort": "4.2.4",
|
|
70
70
|
"@jsenv/ast": "1.4.4",
|
|
71
71
|
"@jsenv/babel-plugins": "1.0.9",
|
|
72
|
-
"@jsenv/filesystem": "4.1.
|
|
72
|
+
"@jsenv/filesystem": "4.1.6",
|
|
73
73
|
"@jsenv/importmap": "1.2.1",
|
|
74
74
|
"@jsenv/integrity": "0.0.1",
|
|
75
75
|
"@jsenv/log": "3.3.1",
|
|
76
76
|
"@jsenv/node-esm-resolution": "0.2.0",
|
|
77
77
|
"@jsenv/server": "14.1.9",
|
|
78
|
-
"@jsenv/sourcemap": "1.0.
|
|
78
|
+
"@jsenv/sourcemap": "1.0.7",
|
|
79
79
|
"@jsenv/uneval": "1.6.0",
|
|
80
80
|
"@jsenv/url-meta": "7.0.0",
|
|
81
|
-
"@jsenv/urls": "1.2.
|
|
81
|
+
"@jsenv/urls": "1.2.8",
|
|
82
82
|
"@jsenv/utils": "2.0.1",
|
|
83
83
|
"acorn-import-assertions": "1.8.0",
|
|
84
84
|
"construct-style-sheets-polyfill": "3.1.0",
|
|
@@ -19,6 +19,14 @@ import { defaultRuntimeCompat } from "@jsenv/core/src/build/build.js"
|
|
|
19
19
|
import { createReloadableWorker } from "@jsenv/core/src/helpers/worker_reload.js"
|
|
20
20
|
import { createFileService } from "./file_service.js"
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Start a server for source files:
|
|
24
|
+
* - cook source files according to jsenv plugins
|
|
25
|
+
* - inject code to autoreload the browser when a file is modified
|
|
26
|
+
* @param {Object} devServerParameters
|
|
27
|
+
* @param {string|url} devServerParameters.rootDirectoryUrl Root directory of the project
|
|
28
|
+
* @return {Object} A dev server object
|
|
29
|
+
*/
|
|
22
30
|
export const startDevServer = async ({
|
|
23
31
|
signal = new AbortController().signal,
|
|
24
32
|
handleSIGINT = true,
|
package/src/main.js
CHANGED
|
@@ -23,8 +23,8 @@ export { startBuildServer } from "./build/start_build_server.js"
|
|
|
23
23
|
|
|
24
24
|
// helpers
|
|
25
25
|
export { pingServer } from "./ping_server.js"
|
|
26
|
-
export { replacePlaceholders } from "./helpers/replace_placeholders.js"
|
|
27
26
|
|
|
28
27
|
// advanced
|
|
29
|
-
export { execute } from "./execute/execute.js"
|
|
30
28
|
export { jsenvPluginInjectGlobals } from "./plugins/inject_globals/jsenv_plugin_inject_globals.js"
|
|
29
|
+
export { jsenvPluginPlaceholders } from "./plugins/placeholders/jsenv_plugin_placeholders.js"
|
|
30
|
+
export { execute } from "./execute/execute.js"
|
|
@@ -1,21 +1,33 @@
|
|
|
1
|
+
import { URL_META } from "@jsenv/url-meta"
|
|
2
|
+
import { asUrlWithoutSearch } from "@jsenv/urls"
|
|
3
|
+
|
|
1
4
|
import { injectGlobals } from "./inject_globals.js"
|
|
2
5
|
|
|
3
|
-
export const jsenvPluginInjectGlobals = (
|
|
6
|
+
export const jsenvPluginInjectGlobals = (rawAssociations) => {
|
|
7
|
+
let resolvedAssociations
|
|
8
|
+
|
|
4
9
|
return {
|
|
5
10
|
name: "jsenv:inject_globals",
|
|
6
11
|
appliesDuring: "*",
|
|
12
|
+
init: (context) => {
|
|
13
|
+
resolvedAssociations = URL_META.resolveAssociations(
|
|
14
|
+
{ injector: rawAssociations },
|
|
15
|
+
context.rootDirectoryUrl,
|
|
16
|
+
)
|
|
17
|
+
},
|
|
7
18
|
transformUrlContent: async (urlInfo, context) => {
|
|
8
|
-
const
|
|
9
|
-
|
|
19
|
+
const { injector } = URL_META.applyAssociations({
|
|
20
|
+
url: asUrlWithoutSearch(urlInfo.url),
|
|
21
|
+
associations: resolvedAssociations,
|
|
10
22
|
})
|
|
11
|
-
if (!
|
|
23
|
+
if (!injector) {
|
|
12
24
|
return null
|
|
13
25
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
globals = await globals(urlInfo, context)
|
|
26
|
+
if (typeof injector !== "function") {
|
|
27
|
+
throw new TypeError("injector must be a function")
|
|
17
28
|
}
|
|
18
|
-
|
|
29
|
+
const globals = await injector(urlInfo, context)
|
|
30
|
+
if (!globals || Object.keys(globals).length === 0) {
|
|
19
31
|
return null
|
|
20
32
|
}
|
|
21
33
|
return injectGlobals(urlInfo, globals)
|
|
@@ -87,6 +87,8 @@ export const jsenvPluginHtmlInlineContent = ({ analyzeConvertedScripts }) => {
|
|
|
87
87
|
if (
|
|
88
88
|
getHtmlNodeAttribute(scriptNode, "jsenv-cooked-by") ===
|
|
89
89
|
"jsenv:supervisor" ||
|
|
90
|
+
getHtmlNodeAttribute(scriptNode, "jsenv-inlined-by") ===
|
|
91
|
+
"jsenv:supervisor" ||
|
|
90
92
|
getHtmlNodeAttribute(scriptNode, "jsenv-injected-by") ===
|
|
91
93
|
"jsenv:supervisor"
|
|
92
94
|
) {
|
|
@@ -37,6 +37,30 @@ export const jsenvPluginMinification = (minification) => {
|
|
|
37
37
|
options: minification.json,
|
|
38
38
|
})
|
|
39
39
|
: null
|
|
40
|
+
const cssOptimizer = minification.css
|
|
41
|
+
? (urlInfo, context) =>
|
|
42
|
+
minifyCss({
|
|
43
|
+
cssUrlInfo: urlInfo,
|
|
44
|
+
context,
|
|
45
|
+
options: minification.css,
|
|
46
|
+
})
|
|
47
|
+
: null
|
|
48
|
+
const jsClassicOptimizer = minification.js_classic
|
|
49
|
+
? (urlInfo, context) =>
|
|
50
|
+
minifyJs({
|
|
51
|
+
jsUrlInfo: urlInfo,
|
|
52
|
+
context,
|
|
53
|
+
options: minification.js_classic,
|
|
54
|
+
})
|
|
55
|
+
: null
|
|
56
|
+
const jsModuleOptimizer = minification.js_module
|
|
57
|
+
? (urlInfo, context) =>
|
|
58
|
+
minifyJs({
|
|
59
|
+
jsUrlInfo: urlInfo,
|
|
60
|
+
context,
|
|
61
|
+
options: minification.js_module,
|
|
62
|
+
})
|
|
63
|
+
: null
|
|
40
64
|
|
|
41
65
|
return {
|
|
42
66
|
name: "jsenv:minification",
|
|
@@ -44,30 +68,9 @@ export const jsenvPluginMinification = (minification) => {
|
|
|
44
68
|
optimizeUrlContent: {
|
|
45
69
|
html: htmlOptimizer,
|
|
46
70
|
svg: htmlOptimizer,
|
|
47
|
-
css:
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
cssUrlInfo: urlInfo,
|
|
51
|
-
context,
|
|
52
|
-
options: minification.css,
|
|
53
|
-
})
|
|
54
|
-
: null,
|
|
55
|
-
js_classic: minification.js_classic
|
|
56
|
-
? (urlInfo, context) =>
|
|
57
|
-
minifyJs({
|
|
58
|
-
jsUrlInfo: urlInfo,
|
|
59
|
-
context,
|
|
60
|
-
options: minification.js_classic,
|
|
61
|
-
})
|
|
62
|
-
: null,
|
|
63
|
-
js_module: minification.js_module
|
|
64
|
-
? (urlInfo, context) =>
|
|
65
|
-
minifyJs({
|
|
66
|
-
jsUrlInfo: urlInfo,
|
|
67
|
-
context,
|
|
68
|
-
options: minification.js_module,
|
|
69
|
-
})
|
|
70
|
-
: null,
|
|
71
|
+
css: cssOptimizer,
|
|
72
|
+
js_classic: jsClassicOptimizer,
|
|
73
|
+
js_module: jsModuleOptimizer,
|
|
71
74
|
json: jsonOptimizer,
|
|
72
75
|
importmap: jsonOptimizer,
|
|
73
76
|
webmanifest: jsonOptimizer,
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { URL_META } from "@jsenv/url-meta"
|
|
2
|
+
import { asUrlWithoutSearch } from "@jsenv/urls"
|
|
3
|
+
|
|
4
|
+
import { replacePlaceholders } from "./replace_placeholders.js"
|
|
5
|
+
|
|
6
|
+
export const jsenvPluginPlaceholders = (rawAssociations) => {
|
|
7
|
+
let resolvedAssociations
|
|
8
|
+
|
|
9
|
+
return {
|
|
10
|
+
name: "jsenv:placeholders",
|
|
11
|
+
appliesDuring: "*",
|
|
12
|
+
init: (context) => {
|
|
13
|
+
resolvedAssociations = URL_META.resolveAssociations(
|
|
14
|
+
{ replacer: rawAssociations },
|
|
15
|
+
context.rootDirectoryUrl,
|
|
16
|
+
)
|
|
17
|
+
},
|
|
18
|
+
transformUrlContent: async (urlInfo, context) => {
|
|
19
|
+
const { replacer } = URL_META.applyAssociations({
|
|
20
|
+
url: asUrlWithoutSearch(urlInfo.url),
|
|
21
|
+
associations: resolvedAssociations,
|
|
22
|
+
})
|
|
23
|
+
if (!replacer) {
|
|
24
|
+
return null
|
|
25
|
+
}
|
|
26
|
+
if (typeof replacer !== "function") {
|
|
27
|
+
throw new TypeError("replacer must be a function")
|
|
28
|
+
}
|
|
29
|
+
const replacements = await replacer(urlInfo, context)
|
|
30
|
+
if (!replacements || Object.keys(replacements).length === 0) {
|
|
31
|
+
return null
|
|
32
|
+
}
|
|
33
|
+
return replacePlaceholders(urlInfo, replacements)
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import { createMagicSource } from "@jsenv/sourcemap"
|
|
2
2
|
|
|
3
|
-
export const replacePlaceholders = (
|
|
3
|
+
export const replacePlaceholders = (urlInfo, replacements) => {
|
|
4
|
+
const content = urlInfo.content
|
|
4
5
|
const magicSource = createMagicSource(content)
|
|
5
6
|
Object.keys(replacements).forEach((key) => {
|
|
6
7
|
let index = content.indexOf(key)
|
|
7
8
|
while (index !== -1) {
|
|
8
9
|
const start = index
|
|
9
10
|
const end = index + key.length
|
|
10
|
-
magicSource.replace({
|
|
11
|
+
magicSource.replace({
|
|
12
|
+
start,
|
|
13
|
+
end,
|
|
14
|
+
replacement:
|
|
15
|
+
urlInfo.type === "js_classic" || urlInfo.type === "js_module"
|
|
16
|
+
? JSON.stringify(replacements[key], null, " ")
|
|
17
|
+
: replacements[key],
|
|
18
|
+
})
|
|
11
19
|
index = content.indexOf(key, end)
|
|
12
20
|
}
|
|
13
21
|
})
|
|
@@ -9,7 +9,7 @@ import { asUrlWithoutSearch } from "@jsenv/urls"
|
|
|
9
9
|
|
|
10
10
|
export const jsenvPluginRibbon = ({
|
|
11
11
|
rootDirectoryUrl,
|
|
12
|
-
htmlInclude = "
|
|
12
|
+
htmlInclude = "/**/*.html",
|
|
13
13
|
}) => {
|
|
14
14
|
const ribbonClientFileUrl = new URL("./client/ribbon.js", import.meta.url)
|
|
15
15
|
const associations = URL_META.resolveAssociations(
|
|
@@ -281,8 +281,9 @@ export const jsenvPluginSupervisor = ({
|
|
|
281
281
|
getHtmlNodeAttribute(node, "jsenv-cooked-by") ||
|
|
282
282
|
getHtmlNodeAttribute(node, "jsenv-inlined-by") ||
|
|
283
283
|
getHtmlNodeAttribute(node, "jsenv-injected-by")
|
|
284
|
-
)
|
|
284
|
+
) {
|
|
285
285
|
return
|
|
286
|
+
}
|
|
286
287
|
const noSupervisor = getHtmlNodeAttribute(node, "no-supervisor")
|
|
287
288
|
if (noSupervisor !== undefined) {
|
|
288
289
|
return
|
|
@@ -104,10 +104,8 @@ const visitHtmlUrls = ({ url, htmlAst }) => {
|
|
|
104
104
|
attributeName,
|
|
105
105
|
specifier,
|
|
106
106
|
}) => {
|
|
107
|
-
const isContentCooked =
|
|
108
|
-
getHtmlNodeAttribute(node, "jsenv-plugin-action") === "content_cooked"
|
|
109
107
|
let position
|
|
110
|
-
if (
|
|
108
|
+
if (getHtmlNodeAttribute(node, "jsenv-cooked-by")) {
|
|
111
109
|
// when generated from inline content,
|
|
112
110
|
// line, column is not "src" nor "inlined-from-src" but "original-position"
|
|
113
111
|
position = getHtmlNodePosition(node)
|
|
@@ -138,8 +136,9 @@ const visitHtmlUrls = ({ url, htmlAst }) => {
|
|
|
138
136
|
const visitAttributeAsUrlSpecifier = ({ node, attributeName, ...rest }) => {
|
|
139
137
|
const value = getHtmlNodeAttribute(node, attributeName)
|
|
140
138
|
if (value) {
|
|
141
|
-
|
|
142
|
-
|
|
139
|
+
if (
|
|
140
|
+
getHtmlNodeAttribute(node, "jsenv-inlined-by") === "jsenv:importmap"
|
|
141
|
+
) {
|
|
143
142
|
// during build the importmap is inlined
|
|
144
143
|
// and shoud not be considered as a dependency anymore
|
|
145
144
|
return null
|