mappel 0.2.0 → 0.3.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 +4 -0
- package/bin/mappel.mjs +11 -5
- package/package.json +1 -1
- package/src/index.mjs +29 -13
package/README.md
CHANGED
|
@@ -8,8 +8,11 @@ It resolves each specifier through the package's own exports map — nested cond
|
|
|
8
8
|
|
|
9
9
|
A repo declares named layers in importmap.config.mjs, and a layer can exclude what another already resolves, so a page loads two maps and neither repeats the other. Layers can also drop a whole scope by prefix. A css specifier maps to a `.js` sibling that adds the file as a link, because a browser cannot import a stylesheet as a module. Pass `--css link` to get the specifier mapped away and the link tags printed instead.
|
|
10
10
|
|
|
11
|
+
Every map is written twice: `name.json` and `name.js`, which installs the map when a page loads it with a script tag. The script must come before the first module script in the page, or it is ignored. Inline maps are registered when their element is inserted, and multiple maps in one page require Chromium 133 or newer.
|
|
12
|
+
|
|
11
13
|
```sh
|
|
12
14
|
mappel # every layer, to the out in the config
|
|
15
|
+
mappel --no-js # the json only, without the script siblings
|
|
13
16
|
mappel --layer min # one layer, to stdout
|
|
14
17
|
mappel --layer min --html # ready to paste into a page
|
|
15
18
|
mappel --layer min --dist-tag alpha # follow a channel instead of pinning
|
|
@@ -26,6 +29,7 @@ export default {
|
|
|
26
29
|
out: 'packages/cdn/dist', // relative to this file
|
|
27
30
|
workspaces: ['packages'],
|
|
28
31
|
split: { components: 'components' }, // a file per package in that layer
|
|
32
|
+
js: false, // skip the script siblings
|
|
29
33
|
layers: {
|
|
30
34
|
base: { packages: ['@scope/core', '@scope/dom'] },
|
|
31
35
|
extra: { packages: ['@scope/extra'], excludes: ['base'] },
|
package/bin/mappel.mjs
CHANGED
|
@@ -5,14 +5,16 @@
|
|
|
5
5
|
// mappel --layer min one layer, to stdout
|
|
6
6
|
// mappel --config packages/cdn/mappel.config.mjs --out dist
|
|
7
7
|
// mappel --layer min --dist-tag alpha --html
|
|
8
|
+
// mappel --no-js maps only, without their script siblings
|
|
8
9
|
//
|
|
9
10
|
// With no arguments it finds mappel.config.mjs by walking up from the current
|
|
10
|
-
// directory, so a package's build script is just `mappel`.
|
|
11
|
+
// directory, so a package's build script is just `mappel`. Every map is written
|
|
12
|
+
// twice: `<name>.json` to read, and `<name>.js` to load from a page.
|
|
11
13
|
|
|
12
14
|
import { existsSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
13
15
|
import { dirname, join, resolve as resolvePath } from 'node:path';
|
|
14
16
|
import { pathToFileURL } from 'node:url';
|
|
15
|
-
import { buildLayer, writeAll, subpathsOf } from '../src/index.mjs';
|
|
17
|
+
import { buildLayer, writeAll, writeMap, subpathsOf } from '../src/index.mjs';
|
|
16
18
|
|
|
17
19
|
const argv = process.argv.slice(2);
|
|
18
20
|
const flag = (name, fallback) => {
|
|
@@ -71,9 +73,12 @@ const options = {
|
|
|
71
73
|
const layerName = flag('layer', null);
|
|
72
74
|
const splitInto = flag('split', null);
|
|
73
75
|
|
|
76
|
+
// Each map gets a script sibling that installs it. `--no-js` writes only JSON.
|
|
77
|
+
const settings = { ...config, js: has('no-js') ? false : has('js') ? true : config.js };
|
|
78
|
+
|
|
74
79
|
if (!layerName && !splitInto) {
|
|
75
80
|
const out = resolvePath(flag('out', config.out ? join(base, config.out) : join(base, 'dist')));
|
|
76
|
-
const written = writeAll(
|
|
81
|
+
const written = writeAll(settings, { ...options, out });
|
|
77
82
|
for (const { file, entries, split } of written) {
|
|
78
83
|
process.stderr.write(` ${file} — ${entries} ${split ? 'files' : 'entries'}\n`);
|
|
79
84
|
}
|
|
@@ -96,7 +101,7 @@ if (splitInto) {
|
|
|
96
101
|
options,
|
|
97
102
|
);
|
|
98
103
|
if (Object.keys(one.imports).length === 0) continue;
|
|
99
|
-
|
|
104
|
+
writeMap(join(dir, name.split('/').pop() + '.json'), one.imports, settings);
|
|
100
105
|
written += 1;
|
|
101
106
|
}
|
|
102
107
|
process.stderr.write(`wrote ${written} maps to ${dir}\n`);
|
|
@@ -108,7 +113,8 @@ const json = JSON.stringify({ imports }, null, 2);
|
|
|
108
113
|
const out = flag('out', null);
|
|
109
114
|
|
|
110
115
|
if (out) {
|
|
111
|
-
|
|
116
|
+
if (out.endsWith('.json')) writeMap(out, imports, settings);
|
|
117
|
+
else writeFileSync(out, json + '\n');
|
|
112
118
|
process.stderr.write(`wrote ${out} — ${Object.keys(imports).length} entries\n`);
|
|
113
119
|
} else if (has('html')) {
|
|
114
120
|
const links =
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mappel",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "generate the import map a page needs from a workspace: resolves every transitive subpath through each package exports map, in layers that compose",
|
|
6
6
|
"type": "module",
|
package/src/index.mjs
CHANGED
|
@@ -130,6 +130,32 @@ function helpers(options) {
|
|
|
130
130
|
|
|
131
131
|
export { subpathsOf, packageDir, resolveExport };
|
|
132
132
|
|
|
133
|
+
/**
|
|
134
|
+
* The script sibling of a map. A page that cannot write JSON into its own
|
|
135
|
+
* markup — a docs site, a playground, anything served as a template — gets the
|
|
136
|
+
* map by loading one classic script instead.
|
|
137
|
+
*
|
|
138
|
+
* An inline map is registered when the element is inserted, so a second file
|
|
139
|
+
* cannot merge into the first one's element and each installs its own. Several
|
|
140
|
+
* maps in one page need Chromium 133 or newer; the combined file is the way to
|
|
141
|
+
* stay on one.
|
|
142
|
+
*/
|
|
143
|
+
function installer(imports) {
|
|
144
|
+
const map = JSON.stringify(JSON.stringify({ imports }));
|
|
145
|
+
return `(function(){var d=document,w=function(m){if(typeof console!=='undefined')console.warn('[mappel] '+m)};
|
|
146
|
+
if(d.querySelector('script[type="module"]'))w('the import map is being added after a module script and will not be used — load this file first');
|
|
147
|
+
var s=d.createElement('script');s.type='importmap';s.setAttribute('data-mappel','');s.textContent=${map};
|
|
148
|
+
s.addEventListener('error',function(){w('the browser rejected this import map — a page with several maps needs Chromium 133+, so use the combined map instead')});
|
|
149
|
+
(d.head||d.documentElement).appendChild(s);
|
|
150
|
+
(self.__mappel||(self.__mappel=[])).push(JSON.parse(s.textContent))})();
|
|
151
|
+
`;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function writeMap(file, imports, config = {}) {
|
|
155
|
+
writeFileSync(file, JSON.stringify({ imports }, null, 2) + '\n');
|
|
156
|
+
if (config.js !== false) writeFileSync(file.replace(/\.json$/, '.js'), installer(imports));
|
|
157
|
+
}
|
|
158
|
+
|
|
133
159
|
/**
|
|
134
160
|
* Write every layer a config declares, plus any per-item split it asks for.
|
|
135
161
|
* This is what `mappel` does with no arguments: the config says where the
|
|
@@ -143,7 +169,7 @@ export function writeAll(config, options) {
|
|
|
143
169
|
for (const name of Object.keys(config.layers)) {
|
|
144
170
|
const { imports } = buildLayer(name, config, options);
|
|
145
171
|
const file = join(out, `${name}.json`);
|
|
146
|
-
|
|
172
|
+
writeMap(file, imports, config);
|
|
147
173
|
written.push({ file, entries: Object.keys(imports).length });
|
|
148
174
|
}
|
|
149
175
|
|
|
@@ -163,14 +189,7 @@ export function writeAll(config, options) {
|
|
|
163
189
|
Object.assign(imports, buildLayer(name, config, options).imports);
|
|
164
190
|
}
|
|
165
191
|
const file = join(out, `${combined}.json`);
|
|
166
|
-
|
|
167
|
-
file,
|
|
168
|
-
JSON.stringify(
|
|
169
|
-
{ imports: Object.fromEntries(Object.entries(imports).sort(([a], [b]) => a.localeCompare(b))) },
|
|
170
|
-
null,
|
|
171
|
-
2,
|
|
172
|
-
) + '\n',
|
|
173
|
-
);
|
|
192
|
+
writeMap(file, sorted(imports), config);
|
|
174
193
|
written.push({ file, entries: Object.keys(imports).length });
|
|
175
194
|
}
|
|
176
195
|
|
|
@@ -193,10 +212,7 @@ export function writeAll(config, options) {
|
|
|
193
212
|
options,
|
|
194
213
|
);
|
|
195
214
|
if (Object.keys(one.imports).length === 0) continue;
|
|
196
|
-
|
|
197
|
-
join(dir, name.split('/').pop() + '.json'),
|
|
198
|
-
JSON.stringify({ imports: one.imports }, null, 2) + '\n',
|
|
199
|
-
);
|
|
215
|
+
writeMap(join(dir, name.split('/').pop() + '.json'), one.imports, config);
|
|
200
216
|
count += 1;
|
|
201
217
|
}
|
|
202
218
|
written.push({ file: `${dir}/*.json`, entries: count, split: true });
|