Skip to content
This repository was archived by the owner on Feb 26, 2025. It is now read-only.

Commit 6f99d47

Browse files
authored
Slim down to a minimal core
This re-focuses the import maps explainer and specification on a minimal core which is more likely to be implementable and agreeable in the near future. It removes multiple import map support, fallback support, and built-in module support for now. The explainer contains a "Further work" section which details these and other potential future work. In terms of normative changes: * The parser now rejects non-string addresses, so arrays and null are no longer valid * The resolver becomes much simpler due to this change, as well as the removal of built-in module support * There is no more procedure for merging import maps * Script fetches only wait on a single "pending import map script" instead of a "list of pending import map scripts"
1 parent 93f94c6 commit 6f99d47

18 files changed

+241
-1372
lines changed

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/reference-implementation/node_modules/
2+
/reference-implementation/coverage/
23
/out/
34
/spec.html
45
/deploy_key

Diff for: README.md

+75-471
Large diffs are not rendered by default.

Diff for: reference-implementation/__tests__/helpers/parsing.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ exports.expectBad = (input, baseURL, warnings = []) => {
3939
checkWarnings();
4040
};
4141

42-
exports.expectWarnings = (input, baseURL, output, warnings = []) => {
42+
exports.expectWarnings = (input, baseURL, output, warnings) => {
4343
const checkWarnings = testWarningHandler(warnings);
4444
expect(parseFromString(input, baseURL)).toEqual(output);
4545

Diff for: reference-implementation/__tests__/parsing-addresses.js

+21-196
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
'use strict';
22
const { expectSpecifierMap } = require('./helpers/parsing.js');
3-
const { BUILT_IN_MODULE_SCHEME } = require('../lib/utils.js');
43

54
describe('Relative URL-like addresses', () => {
65
it('should accept strings prefixed with ./, ../, or /', () => {
@@ -12,9 +11,9 @@ describe('Relative URL-like addresses', () => {
1211
}`,
1312
'https://base.example/path1/path2/path3',
1413
{
15-
dotSlash: [expect.toMatchURL('https://base.example/path1/path2/foo')],
16-
dotDotSlash: [expect.toMatchURL('https://base.example/path1/foo')],
17-
slash: [expect.toMatchURL('https://base.example/foo')]
14+
dotSlash: expect.toMatchURL('https://base.example/path1/path2/foo'),
15+
dotDotSlash: expect.toMatchURL('https://base.example/path1/foo'),
16+
slash: expect.toMatchURL('https://base.example/foo')
1817
}
1918
);
2019
});
@@ -28,9 +27,6 @@ describe('Relative URL-like addresses', () => {
2827
}`,
2928
'data:text/html,test',
3029
{
31-
dotSlash: [],
32-
dotDotSlash: [],
33-
slash: []
3430
},
3531
[
3632
`Invalid address "./foo" for the specifier key "dotSlash".`,
@@ -49,9 +45,9 @@ describe('Relative URL-like addresses', () => {
4945
}`,
5046
'https://base.example/path1/path2/path3',
5147
{
52-
dotSlash: [expect.toMatchURL('https://base.example/path1/path2/')],
53-
dotDotSlash: [expect.toMatchURL('https://base.example/path1/')],
54-
slash: [expect.toMatchURL('https://base.example/')]
48+
dotSlash: expect.toMatchURL('https://base.example/path1/path2/'),
49+
dotDotSlash: expect.toMatchURL('https://base.example/path1/'),
50+
slash: expect.toMatchURL('https://base.example/')
5551
}
5652
);
5753
});
@@ -69,13 +65,6 @@ describe('Relative URL-like addresses', () => {
6965
}`,
7066
'https://base.example/path1/path2/path3',
7167
{
72-
dotSlash1: [],
73-
dotDotSlash1: [],
74-
dotSlash2: [],
75-
dotDotSlash2: [],
76-
slash2: [],
77-
dotSlash3: [],
78-
dotDotSlash3: []
7968
},
8069
[
8170
`Invalid address "%2E/" for the specifier key "dotSlash1".`,
@@ -90,49 +79,6 @@ describe('Relative URL-like addresses', () => {
9079
});
9180
});
9281

93-
describe('Built-in module addresses', () => {
94-
it('should accept URLs using the built-in module scheme', () => {
95-
expectSpecifierMap(
96-
`{
97-
"foo": "${BUILT_IN_MODULE_SCHEME}:foo"
98-
}`,
99-
'https://base.example/path1/path2/path3',
100-
{
101-
foo: [expect.toMatchURL(`${BUILT_IN_MODULE_SCHEME}:foo`)]
102-
}
103-
);
104-
});
105-
106-
it('should ignore percent-encoded variants of the built-in module scheme', () => {
107-
expectSpecifierMap(
108-
`{
109-
"foo": "${encodeURIComponent(BUILT_IN_MODULE_SCHEME + ':')}foo"
110-
}`,
111-
'https://base.example/path1/path2/path3',
112-
{
113-
foo: []
114-
},
115-
[`Invalid address "${encodeURIComponent(BUILT_IN_MODULE_SCHEME + ':')}foo" for the specifier key "foo".`]
116-
);
117-
});
118-
119-
it('should allow built-in module URLs that contain "/" or "\\"', () => {
120-
expectSpecifierMap(
121-
`{
122-
"slashEnd": "${BUILT_IN_MODULE_SCHEME}:foo/",
123-
"slashMiddle": "${BUILT_IN_MODULE_SCHEME}:foo/bar",
124-
"backslash": "${BUILT_IN_MODULE_SCHEME}:foo\\\\baz"
125-
}`,
126-
'https://base.example/path1/path2/path3',
127-
{
128-
slashEnd: [expect.toMatchURL(`${BUILT_IN_MODULE_SCHEME}:foo/`)],
129-
slashMiddle: [expect.toMatchURL(`${BUILT_IN_MODULE_SCHEME}:foo/bar`)],
130-
backslash: [expect.toMatchURL(`${BUILT_IN_MODULE_SCHEME}:foo\\baz`)]
131-
}
132-
);
133-
});
134-
});
135-
13682
describe('Absolute URL addresses', () => {
13783
it('should only accept absolute URL addresses with fetch schemes', () => {
13884
expectSpecifierMap(
@@ -152,58 +98,14 @@ describe('Absolute URL addresses', () => {
15298
}`,
15399
'https://base.example/path1/path2/path3',
154100
{
155-
about: [expect.toMatchURL('about:good')],
156-
blob: [expect.toMatchURL('blob:good')],
157-
data: [expect.toMatchURL('data:good')],
158-
file: [expect.toMatchURL('file:///good')],
159-
filesystem: [expect.toMatchURL('filesystem:good')],
160-
http: [expect.toMatchURL('http://good/')],
161-
https: [expect.toMatchURL('https://good/')],
162-
ftp: [expect.toMatchURL('ftp://good/')],
163-
import: [],
164-
mailto: [],
165-
javascript: [],
166-
wss: []
167-
},
168-
[
169-
`Invalid address "import:bad" for the specifier key "import".`,
170-
`Invalid address "mailto:bad" for the specifier key "mailto".`,
171-
`Invalid address "javascript:bad" for the specifier key "javascript".`,
172-
`Invalid address "wss:bad" for the specifier key "wss".`
173-
]
174-
);
175-
});
176-
177-
it('should only accept absolute URL addresses with fetch schemes inside arrays', () => {
178-
expectSpecifierMap(
179-
`{
180-
"about": ["about:good"],
181-
"blob": ["blob:good"],
182-
"data": ["data:good"],
183-
"file": ["file:///good"],
184-
"filesystem": ["filesystem:good"],
185-
"http": ["http://good/"],
186-
"https": ["https://good/"],
187-
"ftp": ["ftp://good/"],
188-
"import": ["import:bad"],
189-
"mailto": ["mailto:bad"],
190-
"javascript": ["javascript:bad"],
191-
"wss": ["wss:bad"]
192-
}`,
193-
'https://base.example/path1/path2/path3',
194-
{
195-
about: [expect.toMatchURL('about:good')],
196-
blob: [expect.toMatchURL('blob:good')],
197-
data: [expect.toMatchURL('data:good')],
198-
file: [expect.toMatchURL('file:///good')],
199-
filesystem: [expect.toMatchURL('filesystem:good')],
200-
http: [expect.toMatchURL('http://good/')],
201-
https: [expect.toMatchURL('https://good/')],
202-
ftp: [expect.toMatchURL('ftp://good/')],
203-
import: [],
204-
mailto: [],
205-
javascript: [],
206-
wss: []
101+
about: expect.toMatchURL('about:good'),
102+
blob: expect.toMatchURL('blob:good'),
103+
data: expect.toMatchURL('data:good'),
104+
file: expect.toMatchURL('file:///good'),
105+
filesystem: expect.toMatchURL('filesystem:good'),
106+
http: expect.toMatchURL('http://good/'),
107+
https: expect.toMatchURL('https://good/'),
108+
ftp: expect.toMatchURL('ftp://good/')
207109
},
208110
[
209111
`Invalid address "import:bad" for the specifier key "import".`,
@@ -228,45 +130,11 @@ describe('Absolute URL addresses', () => {
228130
}`,
229131
'https://base.example/path1/path2/path3',
230132
{
231-
unparseable1: [],
232-
unparseable2: [],
233-
unparseable3: [],
234-
invalidButParseable1: [expect.toMatchURL('https://example.org/')],
235-
invalidButParseable2: [expect.toMatchURL('https://example.com///')],
236-
prettyNormal: [expect.toMatchURL('https://example.net/')],
237-
percentDecoding: [expect.toMatchURL('https://example.com/')],
238-
noPercentDecoding: [expect.toMatchURL('https://example.com/%41')]
239-
},
240-
[
241-
`Invalid address "https://ex ample.org/" for the specifier key "unparseable1".`,
242-
`Invalid address "https://example.com:demo" for the specifier key "unparseable2".`,
243-
`Invalid address "http://[www.example.com]/" for the specifier key "unparseable3".`
244-
]
245-
);
246-
});
247-
248-
it('should parse absolute URLs, ignoring unparseable ones inside arrays', () => {
249-
expectSpecifierMap(
250-
`{
251-
"unparseable1": ["https://ex ample.org/"],
252-
"unparseable2": ["https://example.com:demo"],
253-
"unparseable3": ["http://[www.example.com]/"],
254-
"invalidButParseable1": ["https:example.org"],
255-
"invalidButParseable2": ["https://///example.com///"],
256-
"prettyNormal": ["https://example.net"],
257-
"percentDecoding": ["https://ex%41mple.com/"],
258-
"noPercentDecoding": ["https://example.com/%41"]
259-
}`,
260-
'https://base.example/path1/path2/path3',
261-
{
262-
unparseable1: [],
263-
unparseable2: [],
264-
unparseable3: [],
265-
invalidButParseable1: [expect.toMatchURL('https://example.org/')],
266-
invalidButParseable2: [expect.toMatchURL('https://example.com///')],
267-
prettyNormal: [expect.toMatchURL('https://example.net/')],
268-
percentDecoding: [expect.toMatchURL('https://example.com/')],
269-
noPercentDecoding: [expect.toMatchURL('https://example.com/%41')]
133+
invalidButParseable1: expect.toMatchURL('https://example.org/'),
134+
invalidButParseable2: expect.toMatchURL('https://example.com///'),
135+
prettyNormal: expect.toMatchURL('https://example.net/'),
136+
percentDecoding: expect.toMatchURL('https://example.com/'),
137+
noPercentDecoding: expect.toMatchURL('https://example.com/%41')
270138
},
271139
[
272140
`Invalid address "https://ex ample.org/" for the specifier key "unparseable1".`,
@@ -281,54 +149,12 @@ describe('Failing addresses: mismatched trailing slashes', () => {
281149
it('should warn for the simple case', () => {
282150
expectSpecifierMap(
283151
`{
284-
"trailer/": "/notrailer",
285-
"${BUILT_IN_MODULE_SCHEME}:trailer/": "/bim-notrailer"
286-
}`,
287-
'https://base.example/path1/path2/path3',
288-
{
289-
'trailer/': [],
290-
[`${BUILT_IN_MODULE_SCHEME}:trailer/`]: []
291-
},
292-
[
293-
`Invalid address "https://base.example/notrailer" for package specifier key "trailer/". Package addresses must end with "/".`,
294-
`Invalid address "https://base.example/bim-notrailer" for package specifier key "${BUILT_IN_MODULE_SCHEME}:trailer/". Package addresses must end with "/".`
295-
]
296-
);
297-
});
298-
299-
it('should warn for a mismatch alone in an array', () => {
300-
expectSpecifierMap(
301-
`{
302-
"trailer/": ["/notrailer"],
303-
"${BUILT_IN_MODULE_SCHEME}:trailer/": ["/bim-notrailer"]
304-
}`,
305-
'https://base.example/path1/path2/path3',
306-
{
307-
'trailer/': [],
308-
[`${BUILT_IN_MODULE_SCHEME}:trailer/`]: []
309-
},
310-
[
311-
`Invalid address "https://base.example/notrailer" for package specifier key "trailer/". Package addresses must end with "/".`,
312-
`Invalid address "https://base.example/bim-notrailer" for package specifier key "${BUILT_IN_MODULE_SCHEME}:trailer/". Package addresses must end with "/".`
313-
]
314-
);
315-
});
316-
317-
it('should warn for a mismatch alongside non-mismatches in an array', () => {
318-
expectSpecifierMap(
319-
`{
320-
"trailer/": ["/atrailer/", "/notrailer"],
321-
"${BUILT_IN_MODULE_SCHEME}:trailer/": ["/bim-atrailer/", "/bim-notrailer"]
152+
"trailer/": "/notrailer"
322153
}`,
323154
'https://base.example/path1/path2/path3',
324155
{
325-
'trailer/': [expect.toMatchURL('https://base.example/atrailer/')],
326-
[`${BUILT_IN_MODULE_SCHEME}:trailer/`]: [expect.toMatchURL('https://base.example/bim-atrailer/')]
327156
},
328-
[
329-
`Invalid address "https://base.example/notrailer" for package specifier key "trailer/". Package addresses must end with "/".`,
330-
`Invalid address "https://base.example/bim-notrailer" for package specifier key "${BUILT_IN_MODULE_SCHEME}:trailer/". Package addresses must end with "/".`
331-
]
157+
[`Invalid address "https://base.example/notrailer" for package specifier key "trailer/". Package addresses must end with "/".`]
332158
);
333159
});
334160
});
@@ -342,7 +168,6 @@ describe('Other invalid addresses', () => {
342168
}`,
343169
'https://base.example/path1/path2/path3',
344170
{
345-
foo: []
346171
},
347172
[`Invalid address "${bad}" for the specifier key "foo".`]
348173
);

Diff for: reference-implementation/__tests__/parsing-schema.js

+6-46
Original file line numberDiff line numberDiff line change
@@ -45,59 +45,35 @@ describe('Mismatching the top-level schema', () => {
4545
});
4646

4747
describe('Mismatching the specifier map schema', () => {
48-
const invalidAddressStrings = ['true', '1', '{}'];
49-
const invalidInsideArrayStrings = ['null', 'true', '1', '{}', '[]'];
48+
const invalidAddressStrings = ['null', 'true', '1', '{}', '[]', '["https://example.com/"]'];
5049

51-
it('should ignore entries where the address is not a string, array, or null', () => {
50+
it('should ignore entries where the address is not a string', () => {
5251
for (const invalid of invalidAddressStrings) {
5352
expectSpecifierMap(
5453
`{
5554
"foo": ${invalid},
56-
"bar": ["https://example.com/"]
55+
"bar": "https://example.com/"
5756
}`,
5857
'https://base.example/',
5958
{
60-
bar: [expect.toMatchURL('https://example.com/')]
59+
bar: expect.toMatchURL('https://example.com/')
6160
},
62-
[
63-
`Invalid address ${invalid} for the specifier key "foo". ` +
64-
`Addresses must be strings, arrays, or null.`
65-
]
61+
[`Invalid address ${invalid} for the specifier key "foo". Addresses must be strings.`]
6662
);
6763
}
6864
});
6965

7066
it('should ignore entries where the specifier key is an empty string', () => {
7167
expectSpecifierMap(
7268
`{
73-
"": ["https://example.com/"]
69+
"": "https://example.com/"
7470
}`,
7571
'https://base.example/',
7672
{},
7773
[`Invalid empty string specifier key.`]
7874
);
7975
});
8076

81-
it('should ignore members of an address array that are not strings', () => {
82-
for (const invalid of invalidInsideArrayStrings) {
83-
expectSpecifierMap(
84-
`{
85-
"foo": ["https://example.com/", ${invalid}],
86-
"bar": ["https://example.com/"]
87-
}`,
88-
'https://base.example/',
89-
{
90-
foo: [expect.toMatchURL('https://example.com/')],
91-
bar: [expect.toMatchURL('https://example.com/')]
92-
},
93-
[
94-
`Invalid address ${invalid} inside the address array for the specifier key "foo". ` +
95-
`Address arrays must only contain strings.`
96-
]
97-
);
98-
}
99-
});
100-
10177
it('should throw if a scope\'s value is not an object', () => {
10278
for (const invalid of nonObjectStrings) {
10379
expectBad(`{ "scopes": { "https://scope.example/": ${invalid} } }`, 'https://base.example/');
@@ -120,20 +96,4 @@ describe('Normalization', () => {
12096
expect(parseFromString(`{ "imports": {} }`, 'https://base.example/'))
12197
.toEqual({ imports: {}, scopes: {} });
12298
});
123-
124-
it('should normalize addresses to arrays', () => {
125-
expectSpecifierMap(
126-
`{
127-
"foo": "https://example.com/1",
128-
"bar": ["https://example.com/2"],
129-
"baz": null
130-
}`,
131-
'https://base.example/',
132-
{
133-
foo: [expect.toMatchURL('https://example.com/1')],
134-
bar: [expect.toMatchURL('https://example.com/2')],
135-
baz: []
136-
}
137-
);
138-
});
13999
});

0 commit comments

Comments
 (0)