mirror of
https://github.com/servo/servo.git
synced 2025-07-11 17:33:47 +01:00
230 lines
11 KiB
JavaScript
230 lines
11 KiB
JavaScript
'use strict';
|
|
|
|
// Imported from:
|
|
// https://github.com/WICG/import-maps/blob/master/reference-implementation/__tests__/resolving.js
|
|
// TODO: Upstream local changes.
|
|
|
|
const { URL } = require('url');
|
|
const { parseFromString } = require('../lib/parser.js');
|
|
const { resolve } = require('../lib/resolver.js');
|
|
|
|
const mapBaseURL = new URL('https://example.com/app/index.html');
|
|
const scriptURL = new URL('https://example.com/js/app.mjs');
|
|
|
|
function makeResolveUnderTest(mapString) {
|
|
const map = parseFromString(mapString, mapBaseURL);
|
|
return specifier => resolve(specifier, map, scriptURL);
|
|
}
|
|
|
|
describe('Unmapped', () => {
|
|
const resolveUnderTest = makeResolveUnderTest(`{}`);
|
|
|
|
it('should resolve ./ specifiers as URLs', () => {
|
|
expect(resolveUnderTest('./foo')).toMatchURL('https://example.com/js/foo');
|
|
expect(resolveUnderTest('./foo/bar')).toMatchURL('https://example.com/js/foo/bar');
|
|
expect(resolveUnderTest('./foo/../bar')).toMatchURL('https://example.com/js/bar');
|
|
expect(resolveUnderTest('./foo/../../bar')).toMatchURL('https://example.com/bar');
|
|
});
|
|
|
|
it('should resolve ../ specifiers as URLs', () => {
|
|
expect(resolveUnderTest('../foo')).toMatchURL('https://example.com/foo');
|
|
expect(resolveUnderTest('../foo/bar')).toMatchURL('https://example.com/foo/bar');
|
|
expect(resolveUnderTest('../../../foo/bar')).toMatchURL('https://example.com/foo/bar');
|
|
});
|
|
|
|
it('should resolve / specifiers as URLs', () => {
|
|
expect(resolveUnderTest('/foo')).toMatchURL('https://example.com/foo');
|
|
expect(resolveUnderTest('/foo/bar')).toMatchURL('https://example.com/foo/bar');
|
|
expect(resolveUnderTest('/../../foo/bar')).toMatchURL('https://example.com/foo/bar');
|
|
expect(resolveUnderTest('/../foo/../bar')).toMatchURL('https://example.com/bar');
|
|
});
|
|
|
|
it('should parse absolute fetch-scheme URLs', () => {
|
|
expect(resolveUnderTest('about:good')).toMatchURL('about:good');
|
|
expect(resolveUnderTest('https://example.net')).toMatchURL('https://example.net/');
|
|
expect(resolveUnderTest('https://ex%41mple.com/')).toMatchURL('https://example.com/');
|
|
expect(resolveUnderTest('https:example.org')).toMatchURL('https://example.org/');
|
|
expect(resolveUnderTest('https://///example.com///')).toMatchURL('https://example.com///');
|
|
});
|
|
|
|
it('should fail for absolute non-fetch-scheme URLs', () => {
|
|
expect(() => resolveUnderTest('mailto:bad')).toThrow(TypeError);
|
|
expect(() => resolveUnderTest('import:bad')).toThrow(TypeError);
|
|
expect(() => resolveUnderTest('javascript:bad')).toThrow(TypeError);
|
|
expect(() => resolveUnderTest('wss:bad')).toThrow(TypeError);
|
|
});
|
|
|
|
it('should fail for strings not parseable as absolute URLs and not starting with ./ ../ or /', () => {
|
|
expect(() => resolveUnderTest('foo')).toThrow(TypeError);
|
|
expect(() => resolveUnderTest('\\foo')).toThrow(TypeError);
|
|
expect(() => resolveUnderTest(':foo')).toThrow(TypeError);
|
|
expect(() => resolveUnderTest('@foo')).toThrow(TypeError);
|
|
expect(() => resolveUnderTest('%2E/foo')).toThrow(TypeError);
|
|
expect(() => resolveUnderTest('%2E%2E/foo')).toThrow(TypeError);
|
|
expect(() => resolveUnderTest('.%2Ffoo')).toThrow(TypeError);
|
|
expect(() => resolveUnderTest('https://ex ample.org/')).toThrow(TypeError);
|
|
expect(() => resolveUnderTest('https://example.com:demo')).toThrow(TypeError);
|
|
expect(() => resolveUnderTest('http://[www.example.com]/')).toThrow(TypeError);
|
|
});
|
|
});
|
|
|
|
describe('Mapped using the "imports" key only (no scopes)', () => {
|
|
it('should fail when the mapping is to an empty array', () => {
|
|
const resolveUnderTest = makeResolveUnderTest(`{
|
|
"imports": {
|
|
"moment": null,
|
|
"lodash": []
|
|
}
|
|
}`);
|
|
|
|
expect(() => resolveUnderTest('moment')).toThrow(TypeError);
|
|
expect(() => resolveUnderTest('lodash')).toThrow(TypeError);
|
|
});
|
|
|
|
describe('Package-like scenarios', () => {
|
|
const resolveUnderTest = makeResolveUnderTest(`{
|
|
"imports": {
|
|
"moment": "/node_modules/moment/src/moment.js",
|
|
"moment/": "/node_modules/moment/src/",
|
|
"lodash-dot": "./node_modules/lodash-es/lodash.js",
|
|
"lodash-dot/": "./node_modules/lodash-es/",
|
|
"lodash-dotdot": "../node_modules/lodash-es/lodash.js",
|
|
"lodash-dotdot/": "../node_modules/lodash-es/"
|
|
}
|
|
}`);
|
|
|
|
it('should work for package main modules', () => {
|
|
expect(resolveUnderTest('moment')).toMatchURL('https://example.com/node_modules/moment/src/moment.js');
|
|
expect(resolveUnderTest('lodash-dot')).toMatchURL('https://example.com/app/node_modules/lodash-es/lodash.js');
|
|
expect(resolveUnderTest('lodash-dotdot')).toMatchURL('https://example.com/node_modules/lodash-es/lodash.js');
|
|
});
|
|
|
|
it('should work for package submodules', () => {
|
|
expect(resolveUnderTest('moment/foo')).toMatchURL('https://example.com/node_modules/moment/src/foo');
|
|
expect(resolveUnderTest('lodash-dot/foo')).toMatchURL('https://example.com/app/node_modules/lodash-es/foo');
|
|
expect(resolveUnderTest('lodash-dotdot/foo')).toMatchURL('https://example.com/node_modules/lodash-es/foo');
|
|
});
|
|
|
|
it('should work for package names that end in a slash by just passing through', () => {
|
|
// TODO: is this the right behavior, or should we throw?
|
|
expect(resolveUnderTest('moment/')).toMatchURL('https://example.com/node_modules/moment/src/');
|
|
});
|
|
|
|
it('should still fail for package modules that are not declared', () => {
|
|
expect(() => resolveUnderTest('underscore/')).toThrow(TypeError);
|
|
expect(() => resolveUnderTest('underscore/foo')).toThrow(TypeError);
|
|
});
|
|
});
|
|
|
|
describe('Tricky specifiers', () => {
|
|
const resolveUnderTest = makeResolveUnderTest(`{
|
|
"imports": {
|
|
"package/withslash": "/node_modules/package-with-slash/index.mjs",
|
|
"not-a-package": "/lib/not-a-package.mjs",
|
|
".": "/lib/dot.mjs",
|
|
"..": "/lib/dotdot.mjs",
|
|
"..\\\\": "/lib/dotdotbackslash.mjs",
|
|
"%2E": "/lib/percent2e.mjs",
|
|
"%2F": "/lib/percent2f.mjs"
|
|
}
|
|
}`);
|
|
|
|
it('should work for explicitly-mapped specifiers that happen to have a slash', () => {
|
|
expect(resolveUnderTest('package/withslash')).toMatchURL('https://example.com/node_modules/package-with-slash/index.mjs');
|
|
});
|
|
|
|
it('should work when the specifier has punctuation', () => {
|
|
expect(resolveUnderTest('.')).toMatchURL('https://example.com/lib/dot.mjs');
|
|
expect(resolveUnderTest('..')).toMatchURL('https://example.com/lib/dotdot.mjs');
|
|
expect(resolveUnderTest('..\\')).toMatchURL('https://example.com/lib/dotdotbackslash.mjs');
|
|
expect(resolveUnderTest('%2E')).toMatchURL('https://example.com/lib/percent2e.mjs');
|
|
expect(resolveUnderTest('%2F')).toMatchURL('https://example.com/lib/percent2f.mjs');
|
|
});
|
|
|
|
it('should fail for attempting to get a submodule of something not declared with a trailing slash', () => {
|
|
expect(() => resolveUnderTest('not-a-package/foo')).toThrow(TypeError);
|
|
});
|
|
});
|
|
|
|
describe('URL-like specifiers', () => {
|
|
const resolveUnderTest = makeResolveUnderTest(`{
|
|
"imports": {
|
|
"/node_modules/als-polyfill/index.mjs": "@std/kv-storage",
|
|
|
|
"/lib/foo.mjs": "./more/bar.mjs",
|
|
"./dotrelative/foo.mjs": "/lib/dot.mjs",
|
|
"../dotdotrelative/foo.mjs": "/lib/dotdot.mjs",
|
|
|
|
"/lib/no.mjs": null,
|
|
"./dotrelative/no.mjs": [],
|
|
|
|
"/": "/lib/slash-only.mjs",
|
|
"./": "/lib/dotslash-only.mjs",
|
|
|
|
"/test": "/lib/test1.mjs",
|
|
"../test": "/lib/test2.mjs"
|
|
}
|
|
}`);
|
|
|
|
it('should remap to built-in modules', () => {
|
|
expect(resolveUnderTest('/node_modules/als-polyfill/index.mjs')).toMatchURL('import:@std/kv-storage');
|
|
expect(resolveUnderTest('https://example.com/node_modules/als-polyfill/index.mjs')).toMatchURL('import:@std/kv-storage');
|
|
expect(resolveUnderTest('https://///example.com/node_modules/als-polyfill/index.mjs')).toMatchURL('import:@std/kv-storage');
|
|
});
|
|
|
|
it('should remap to other URLs', () => {
|
|
expect(resolveUnderTest('https://example.com/lib/foo.mjs')).toMatchURL('https://example.com/app/more/bar.mjs');
|
|
expect(resolveUnderTest('https://///example.com/lib/foo.mjs')).toMatchURL('https://example.com/app/more/bar.mjs');
|
|
expect(resolveUnderTest('/lib/foo.mjs')).toMatchURL('https://example.com/app/more/bar.mjs');
|
|
|
|
expect(resolveUnderTest('https://example.com/app/dotrelative/foo.mjs')).toMatchURL('https://example.com/lib/dot.mjs');
|
|
expect(resolveUnderTest('../app/dotrelative/foo.mjs')).toMatchURL('https://example.com/lib/dot.mjs');
|
|
|
|
expect(resolveUnderTest('https://example.com/dotdotrelative/foo.mjs')).toMatchURL('https://example.com/lib/dotdot.mjs');
|
|
expect(resolveUnderTest('../dotdotrelative/foo.mjs')).toMatchURL('https://example.com/lib/dotdot.mjs');
|
|
});
|
|
|
|
it('should fail for URLs that remap to empty arrays', () => {
|
|
expect(() => resolveUnderTest('https://example.com/lib/no.mjs')).toThrow(TypeError);
|
|
expect(() => resolveUnderTest('/lib/no.mjs')).toThrow(TypeError);
|
|
expect(() => resolveUnderTest('../lib/no.mjs')).toThrow(TypeError);
|
|
|
|
expect(() => resolveUnderTest('https://example.com/app/dotrelative/no.mjs')).toThrow(TypeError);
|
|
expect(() => resolveUnderTest('/app/dotrelative/no.mjs')).toThrow(TypeError);
|
|
expect(() => resolveUnderTest('../app/dotrelative/no.mjs')).toThrow(TypeError);
|
|
});
|
|
|
|
it('should remap URLs that are just composed from / and .', () => {
|
|
expect(resolveUnderTest('https://example.com/')).toMatchURL('https://example.com/lib/slash-only.mjs');
|
|
expect(resolveUnderTest('/')).toMatchURL('https://example.com/lib/slash-only.mjs');
|
|
expect(resolveUnderTest('../')).toMatchURL('https://example.com/lib/slash-only.mjs');
|
|
|
|
expect(resolveUnderTest('https://example.com/app/')).toMatchURL('https://example.com/lib/dotslash-only.mjs');
|
|
expect(resolveUnderTest('/app/')).toMatchURL('https://example.com/lib/dotslash-only.mjs');
|
|
expect(resolveUnderTest('../app/')).toMatchURL('https://example.com/lib/dotslash-only.mjs');
|
|
});
|
|
|
|
it('should use the last entry\'s address when URL-like specifiers parse to the same absolute URL', () => {
|
|
expect(resolveUnderTest('/test')).toMatchURL('https://example.com/lib/test2.mjs');
|
|
});
|
|
});
|
|
|
|
describe('overlapping entries with trailing slashes', () => {
|
|
const resolveUnderTest = makeResolveUnderTest(`{
|
|
"imports": {
|
|
"a": "/1",
|
|
"a/": "/2/",
|
|
"a/b": "/3",
|
|
"a/b/": "/4/"
|
|
}
|
|
}`);
|
|
|
|
it('most-specific wins', () => {
|
|
expect(resolveUnderTest('a')).toMatchURL('https://example.com/1');
|
|
expect(resolveUnderTest('a/')).toMatchURL('https://example.com/2/');
|
|
expect(resolveUnderTest('a/b')).toMatchURL('https://example.com/3');
|
|
expect(resolveUnderTest('a/b/')).toMatchURL('https://example.com/4/');
|
|
expect(resolveUnderTest('a/b/c')).toMatchURL('https://example.com/4/c');
|
|
});
|
|
});
|
|
});
|