Skip to content

Commit

Permalink
handle scope normalization as base-relative (#2493)
Browse files Browse the repository at this point in the history
  • Loading branch information
guybedford authored Jul 31, 2019
1 parent cd6ba8a commit 85d7896
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 70 deletions.
17 changes: 9 additions & 8 deletions src/map/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ function resolvePackages(pkgs, baseUrl) {
// TODO package fallback support
if (typeof value !== 'string')
continue;
outPkgs[resolveIfNotPlainOrUrl(p, baseUrl) || p] = value;
outPkgs[resolveIfNotPlainOrUrl(p, baseUrl) || p] = resolveUrl(value, baseUrl);
}
return outPkgs;
}
Expand All @@ -142,11 +142,11 @@ export function parseImportMap (json, baseUrl) {
let resolvedScopeName = resolveUrl(scopeName, baseUrl);
if (resolvedScopeName[resolvedScopeName.length - 1] !== '/')
resolvedScopeName += '/';
scopes[resolvedScopeName] = resolvePackages(scope, resolvedScopeName) || {};
scopes[resolvedScopeName] = resolvePackages(scope, baseUrl) || {};
}
}

return { imports: imports, scopes: scopes, baseUrl: baseUrl };
return { imports: imports, scopes: scopes };
}

function getMatch (path, matchObj) {
Expand All @@ -160,15 +160,16 @@ function getMatch (path, matchObj) {
} while ((sepIndex = path.lastIndexOf('/', sepIndex - 1)) !== -1)
}

function applyPackages (id, packages, baseUrl) {
function applyPackages (id, packages) {
const pkgName = getMatch(id, packages);
if (pkgName) {
const pkg = packages[pkgName];
if (pkg === null)

return;
if (id.length > pkgName.length && pkg[pkg.length - 1] !== '/')
console.warn("Invalid package target " + pkg + " for '" + pkgName + "' should have a trailing '/'.");
return resolveUrl(pkg + id.slice(pkgName.length), baseUrl);
const subpath = id.slice(pkgName.length);
return subpath ? resolveUrl(subpath, pkg) : pkg;
}
}

Expand All @@ -179,11 +180,11 @@ export function resolveImportMap (id, parentUrl, importMap) {
const scopeName = getMatch(parentUrl, importMap.scopes);
if (scopeName) {
const scopePackages = importMap.scopes[scopeName];
const packageResolution = applyPackages(id, scopePackages, scopeName);
const packageResolution = applyPackages(id, scopePackages);
if (packageResolution)
return packageResolution;
}
return applyPackages(id, importMap.imports, importMap.baseUrl) || urlResolved || throwBare(id, parentUrl);
return applyPackages(id, importMap.imports) || urlResolved || throwBare(id, parentUrl);
}

export function throwBare (id, parentUrl) {
Expand Down
36 changes: 7 additions & 29 deletions src/map/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,23 +109,16 @@ class Mapper {

const pkgPath = `jspm_packages/${pkgName.replace(':', '/')}`;
const packages = scopeParent ? (packageMap.scopes[scopeParent] = (packageMap.scopes[scopeParent] || {})) : packageMap.imports;
let pkgRelative = (scopeParent ? path.relative(scopeParent, pkgPath).replace(/\\/g, '/') : pkgPath);
if (!pkgRelative.startsWith('../'))
pkgRelative = './' + pkgRelative;
const curPkg = packages[depName + '/'] = pkgRelative + '/';
const curPkg = packages[depName + '/'] = './' + pkgPath + '/';
const pkg = this.project.config.jspm.installed.dependencies[pkgName];

const pathsPromise = (async () => {
const { name, main, paths, map } = await this.getPackageConfig(pkgName);

if (main)
packages[depName] = curPkg + main;
for (const subpath of Object.keys(paths)) {
let relPath = scopeParent ? path.relative(scopeParent, pkgPath).replace(/\\/g, '/') : pkgPath;
if (!relPath.startsWith('../'))
relPath = './' + relPath;
packages[depName + '/' + subpath] = relPath + '/' + paths[subpath];
}
for (const subpath of Object.keys(paths))
packages[depName + '/' + subpath] = './' + pkgPath + '/' + paths[subpath];

if (seen[pkgName + '|map'])
return;
Expand All @@ -134,12 +127,8 @@ class Mapper {
const scopedPackages = (packageMap.scopes[pkgPath + '/'] = (packageMap.scopes[pkgPath + '/'] || {}));

// scopedPackages[name + '/']
for (const subpath of Object.keys(paths)) {
let target = path.relative(name, name + '/' + paths[subpath]).replace(/\\/g, '/');
if (!target.startsWith('../'))
target = './' + target;
scopedPackages[name + '/' + subpath] = target;
}
for (const subpath of Object.keys(paths))
scopedPackages[name + '/' + subpath] = './' + name + '/' + paths[subpath];

for (const target of Object.keys(map)) {
let mapped = map[target];
Expand All @@ -156,11 +145,7 @@ class Mapper {
mapped = `${this.nodeBuiltinsPkg}/${mapped}.js`;
}
}

let output = path.relative(pkgPath + '/', mapped).replace(/\\/g, '/');
if (!output.startsWith('../'))
output = './' + output;
scopedPackages[target] = output;
scopedPackages[target] = './' + mapped;
}
})();

Expand Down Expand Up @@ -252,15 +237,8 @@ export function renormalizeMap (map: ImportMap, jspmPackagesURL: string, cdn: bo
let scopeRegistry = scopeName.substr(14);
scopeRegistry = scopeRegistry.substr(0, scopeRegistry.indexOf('/'));

const isScopedPackage = scopeName.indexOf('/', scopeName.indexOf('/', 14) + 1) !== scopeName.length - 1;

for (const pkgName of Object.keys(scope)) {
let pkg = scope[pkgName];
if (cdn && pkg.startsWith('../')) {
// exception is within-scope backtracking
if (!(isScopedPackage && pkg.startsWith('../') && !pkg.startsWith('../../')))
pkg = pkg.replace(/^((\.\.\/)+)(.+)$/, `$1${scopeRegistry}:$3`);
}
const pkg = scope[pkgName];
newScope[pkgName] = (cdn ? cdnReplace(pkg) : pkg).replace(/^(\.\/)+jspm_packages/, jspmPackagesURL);
}
newMap.scopes[jspmPackagesURL + (cdn ? cdnReplace(scopeName) : scopeName).substr(13)] = newScope;
Expand Down
54 changes: 21 additions & 33 deletions src/map/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,28 +99,28 @@ export function rebaseMap (map: ImportMap, fromPath: string, toPath: string, abs
for (const scopeName of Object.keys(map.scopes)) {
const scope = map.scopes[scopeName];
const newScope = Object.create(null);
if (isURL(scopeName, true)) {
newMap.scopes[scopeName] = Object.assign(newScope, scope);
}
else {
let rebasedScope = scopeName;
if (!isURL(scopeName, true)) {
const resolvedScope = path.resolve(fromPath, scopeName);
let rebasedScope = path.relative(toPath, resolvedScope).replace(/\\/g, '/') + '/';
for (const pkgName of Object.keys(scope)) {
const pkg = scope[pkgName];
let rebased = isURL(pkg, true) ? pkg : path.relative(resolvedScope, path.resolve(resolvedScope, pkg)).replace(/\\/g, '/');
if (!rebased.startsWith('../'))
rebased = './' + rebased;
if (pkg.endsWith('/'))
rebased += '/';
newScope[pkgName] = rebased;
}
rebasedScope = path.relative(toPath, resolvedScope).replace(/\\/g, '/') + '/';
if (absolute) {
if (rebasedScope.startsWith('../'))
throw new JspmUserError(`Unable to reference scope ${scopeName} at ${newScope}. The base for the import map must a higher path than its mappings.`);
throw new JspmUserError(`Unable to reference scope ${scopeName} at ${resolvedScope}. The base for the import map must a higher path than its mappings.`);
rebasedScope = prefix + rebasedScope;
}
newMap.scopes[rebasedScope] = newScope;
}
for (const pkgName of Object.keys(scope)) {
const pkg = scope[pkgName];
let rebased = isURL(pkg, true) ? pkg : path.relative(toPath, path.resolve(fromPath, pkg)).replace(/\\/g, '/');
if (pkg.endsWith('/'))
rebased += '/';
if (!rebased.startsWith('../'))
rebased = prefix + rebased;
else if (absolute)
throw new JspmUserError(`Unable to reference mapping ${pkgName} at ${rebased} in scope ${scopeName}. The base for the import map must a higher path than its mappings.`);
newScope[pkgName] = rebased;
}
newMap.scopes[rebasedScope] = newScope;
}
}
return newMap;
Expand All @@ -134,15 +134,9 @@ export function flattenScopes (importMap: ImportMap) {
for (const pkgName of Object.keys(imports)) {
const existing = importMap.imports[pkgName];
const newTarget = imports[pkgName];
const trailingSlash = newTarget.endsWith('/');
let newTargetResolved = path.relative('.', path.resolve(scope, newTarget)).replace(/\\/g, '/');
if (!newTargetResolved.startsWith('../'))
newTargetResolved = './' + newTargetResolved;
if (trailingSlash)
newTargetResolved += '/';
if (existing && existing !== newTargetResolved)
throw new JspmUserError(`Cannot flatten scopes due to conflict for ${bold(pkgName)} between ${existing} and ${newTargetResolved}.`);
importMap.imports[pkgName] = newTargetResolved;
if (existing && existing !== newTarget)
throw new JspmUserError(`Cannot flatten scopes due to conflict for ${bold(pkgName)} between ${existing} and ${newTarget}.`);
importMap.imports[pkgName] = newTarget;
}
}
delete importMap.scopes;
Expand All @@ -155,14 +149,8 @@ export function clean (importMap: ImportMap) {
for (const scope of Object.keys(importMap.scopes)) {
const imports = importMap.scopes[scope];
for (const pkgName of Object.keys(imports)) {
if (pkgName.startsWith('./') || pkgName.startsWith('../'))
continue;
let baseMap = importMap.imports[pkgName];
if (!baseMap)
continue;
let map = imports[pkgName];
// TODO: handle URL-like
if (path.join(pkgName, baseMap) === path.join(scope, pkgName, map))
// renormalization in check?
if (importMap.imports[pkgName] === imports[pkgName])
delete imports[pkgName];
}
}
Expand Down

0 comments on commit 85d7896

Please sign in to comment.