Files
open-wc/packages/import-maps-resolve/test/resolving-scopes.test.js

278 lines
10 KiB
JavaScript

import chai from 'chai';
import { URL } from 'url';
import { parseFromString } from '../src/parser.js';
import { resolve } from '../src/resolver.js';
const { expect } = chai;
const mapBaseURL = 'https://example.com/app/index.html';
function makeResolveUnderTest(mapString) {
const map = parseFromString(mapString, mapBaseURL);
return (specifier, baseURL) => resolve(specifier, map, baseURL);
}
describe('Mapped using scope instead of "imports"', () => {
const jsNonDirURL = 'https://example.com/js';
const jsPrefixedURL = 'https://example.com/jsiscool';
const inJSDirURL = 'https://example.com/js/app.mjs';
const topLevelURL = 'https://example.com/app.mjs';
it('should fail when the mapping is to an empty array', () => {
const resolveUnderTest = makeResolveUnderTest(`{
"scopes": {
"/js/": {
"moment": null,
"lodash": []
}
}
}`);
expect(() => resolveUnderTest('moment', inJSDirURL)).to.throw(TypeError);
expect(() => resolveUnderTest('lodash', inJSDirURL)).to.throw(TypeError);
});
describe('Exact vs. prefix based matching', () => {
it('should match correctly when both are in the map', () => {
const resolveUnderTest = makeResolveUnderTest(`{
"scopes": {
"/js": {
"moment": "/only-triggered-by-exact/moment",
"moment/": "/only-triggered-by-exact/moment/"
},
"/js/": {
"moment": "/triggered-by-any-subpath/moment",
"moment/": "/triggered-by-any-subpath/moment/"
}
}
}`);
expect(resolveUnderTest('moment', jsNonDirURL)).to.equal(
'https://example.com/only-triggered-by-exact/moment',
);
expect(resolveUnderTest('moment/foo', jsNonDirURL)).to.equal(
'https://example.com/only-triggered-by-exact/moment/foo',
);
expect(resolveUnderTest('moment', inJSDirURL)).to.equal(
'https://example.com/triggered-by-any-subpath/moment',
);
expect(resolveUnderTest('moment/foo', inJSDirURL)).to.equal(
'https://example.com/triggered-by-any-subpath/moment/foo',
);
expect(() => resolveUnderTest('moment', jsPrefixedURL)).to.throw(TypeError);
expect(() => resolveUnderTest('moment/foo', jsPrefixedURL)).to.throw(TypeError);
});
it('should match correctly when only an exact match is in the map', () => {
const resolveUnderTest = makeResolveUnderTest(`{
"scopes": {
"/js": {
"moment": "/only-triggered-by-exact/moment",
"moment/": "/only-triggered-by-exact/moment/"
}
}
}`);
expect(resolveUnderTest('moment', jsNonDirURL)).to.equal(
'https://example.com/only-triggered-by-exact/moment',
);
expect(resolveUnderTest('moment/foo', jsNonDirURL)).to.equal(
'https://example.com/only-triggered-by-exact/moment/foo',
);
expect(() => resolveUnderTest('moment', inJSDirURL)).to.throw(TypeError);
expect(() => resolveUnderTest('moment/foo', inJSDirURL)).to.throw(TypeError);
expect(() => resolveUnderTest('moment', jsPrefixedURL)).to.throw(TypeError);
expect(() => resolveUnderTest('moment/foo', jsPrefixedURL)).to.throw(TypeError);
});
it('should match correctly when only a prefix match is in the map', () => {
const resolveUnderTest = makeResolveUnderTest(`{
"scopes": {
"/js/": {
"moment": "/triggered-by-any-subpath/moment",
"moment/": "/triggered-by-any-subpath/moment/"
}
}
}`);
expect(() => resolveUnderTest('moment', jsNonDirURL)).to.throw(TypeError);
expect(() => resolveUnderTest('moment/foo', jsNonDirURL)).to.throw(TypeError);
expect(resolveUnderTest('moment', inJSDirURL)).to.equal(
'https://example.com/triggered-by-any-subpath/moment',
);
expect(resolveUnderTest('moment/foo', inJSDirURL)).to.equal(
'https://example.com/triggered-by-any-subpath/moment/foo',
);
expect(() => resolveUnderTest('moment', jsPrefixedURL)).to.throw(TypeError);
expect(() => resolveUnderTest('moment/foo', jsPrefixedURL)).to.throw(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/"
},
"scopes": {
"/": {
"moment": "/node_modules_3/moment/src/moment.js",
"vue": "/node_modules_3/vue/dist/vue.runtime.esm.js"
},
"/js/": {
"lodash-dot": "./node_modules_2/lodash-es/lodash.js",
"lodash-dot/": "./node_modules_2/lodash-es/",
"lodash-dotdot": "../node_modules_2/lodash-es/lodash.js",
"lodash-dotdot/": "../node_modules_2/lodash-es/"
}
}
}`);
it('should resolve scoped', () => {
expect(resolveUnderTest('lodash-dot', inJSDirURL)).to.equal(
'https://example.com/app/node_modules_2/lodash-es/lodash.js',
);
expect(resolveUnderTest('lodash-dotdot', inJSDirURL)).to.equal(
'https://example.com/node_modules_2/lodash-es/lodash.js',
);
expect(resolveUnderTest('lodash-dot/foo', inJSDirURL)).to.equal(
'https://example.com/app/node_modules_2/lodash-es/foo',
);
expect(resolveUnderTest('lodash-dotdot/foo', inJSDirURL)).to.equal(
'https://example.com/node_modules_2/lodash-es/foo',
);
});
it('should apply best scope match', () => {
expect(resolveUnderTest('moment', topLevelURL)).to.equal(
'https://example.com/node_modules_3/moment/src/moment.js',
);
expect(resolveUnderTest('moment', inJSDirURL)).to.equal(
'https://example.com/node_modules_3/moment/src/moment.js',
);
expect(resolveUnderTest('vue', inJSDirURL)).to.equal(
'https://example.com/node_modules_3/vue/dist/vue.runtime.esm.js',
);
});
it('should fallback to "imports"', () => {
expect(resolveUnderTest('moment/foo', topLevelURL)).to.equal(
'https://example.com/node_modules/moment/src/foo',
);
expect(resolveUnderTest('moment/foo', inJSDirURL)).to.equal(
'https://example.com/node_modules/moment/src/foo',
);
expect(resolveUnderTest('lodash-dot', topLevelURL)).to.equal(
'https://example.com/app/node_modules/lodash-es/lodash.js',
);
expect(resolveUnderTest('lodash-dotdot', topLevelURL)).to.equal(
'https://example.com/node_modules/lodash-es/lodash.js',
);
expect(resolveUnderTest('lodash-dot/foo', topLevelURL)).to.equal(
'https://example.com/app/node_modules/lodash-es/foo',
);
expect(resolveUnderTest('lodash-dotdot/foo', topLevelURL)).to.equal(
'https://example.com/node_modules/lodash-es/foo',
);
});
it('should still fail for package-like specifiers that are not declared', () => {
expect(() => resolveUnderTest('underscore/', inJSDirURL)).to.throw(TypeError);
expect(() => resolveUnderTest('underscore/foo', inJSDirURL)).to.throw(TypeError);
});
});
describe('The scope inheritance example from the README', () => {
const resolveUnderTest = makeResolveUnderTest(`{
"imports": {
"a": "/a-1.mjs",
"b": "/b-1.mjs",
"c": "/c-1.mjs"
},
"scopes": {
"/scope2/": {
"a": "/a-2.mjs"
},
"/scope2/scope3/": {
"b": "/b-3.mjs"
}
}
}`);
const scope1URL = 'https://example.com/scope1/foo.mjs';
const scope2URL = 'https://example.com/scope2/foo.mjs';
const scope3URL = 'https://example.com/scope2/scope3/foo.mjs';
it('should fall back to "imports" when none match', () => {
expect(resolveUnderTest('a', scope1URL)).to.equal('https://example.com/a-1.mjs');
expect(resolveUnderTest('b', scope1URL)).to.equal('https://example.com/b-1.mjs');
expect(resolveUnderTest('c', scope1URL)).to.equal('https://example.com/c-1.mjs');
});
it('should use a direct scope override', () => {
expect(resolveUnderTest('a', scope2URL)).to.equal('https://example.com/a-2.mjs');
expect(resolveUnderTest('b', scope2URL)).to.equal('https://example.com/b-1.mjs');
expect(resolveUnderTest('c', scope2URL)).to.equal('https://example.com/c-1.mjs');
});
it('should use an indirect scope override', () => {
expect(resolveUnderTest('a', scope3URL)).to.equal('https://example.com/a-2.mjs');
expect(resolveUnderTest('b', scope3URL)).to.equal('https://example.com/b-3.mjs');
expect(resolveUnderTest('c', scope3URL)).to.equal('https://example.com/c-1.mjs');
});
});
describe('Relative URL scope keys', () => {
const resolveUnderTest = makeResolveUnderTest(`{
"imports": {
"a": "/a-1.mjs",
"b": "/b-1.mjs",
"c": "/c-1.mjs"
},
"scopes": {
"": {
"a": "/a-empty-string.mjs"
},
"./": {
"b": "/b-dot-slash.mjs"
},
"../": {
"c": "/c-dot-dot-slash.mjs"
}
}
}`);
const inSameDirAsMap = new URL('./foo.mjs', mapBaseURL).href;
const inDirAboveMap = new URL('../foo.mjs', mapBaseURL).href;
it('should resolve an empty string scope using the import map URL', () => {
expect(resolveUnderTest('a', mapBaseURL)).to.equal('https://example.com/a-empty-string.mjs');
expect(resolveUnderTest('a', inSameDirAsMap)).to.equal('https://example.com/a-1.mjs');
});
it("should resolve a ./ scope using the import map URL's directory", () => {
expect(resolveUnderTest('b', mapBaseURL)).to.equal('https://example.com/b-dot-slash.mjs');
expect(resolveUnderTest('b', inSameDirAsMap)).to.equal('https://example.com/b-dot-slash.mjs');
});
it("should resolve a ../ scope using the import map URL's directory", () => {
expect(resolveUnderTest('c', mapBaseURL)).to.equal('https://example.com/c-dot-dot-slash.mjs');
expect(resolveUnderTest('c', inSameDirAsMap)).to.equal(
'https://example.com/c-dot-dot-slash.mjs',
);
expect(resolveUnderTest('c', inDirAboveMap)).to.equal(
'https://example.com/c-dot-dot-slash.mjs',
);
});
});
});