Skip to content

Commit

Permalink
Merge pull request #1069 from embroider-build/plain-addon-shim
Browse files Browse the repository at this point in the history
Make addon-shim a non-ember-addon
  • Loading branch information
ef4 committed Jan 8, 2022
2 parents 8c9e56a + 60c4446 commit 30373fd
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 60 deletions.
12 changes: 0 additions & 12 deletions packages/addon-shim/addon-main.js

This file was deleted.

8 changes: 1 addition & 7 deletions packages/addon-shim/package.json
Expand Up @@ -2,9 +2,7 @@
"name": "@embroider/addon-shim",
"version": "0.49.0",
"description": "Make v2 addons work in non-Embroider apps.",
"keywords": [
"ember-addon"
],
"keywords": [],
"main": "./src/index.js",
"repository": {
"type": "git",
Expand All @@ -22,7 +20,6 @@
},
"dependencies": {
"@embroider/shared-internals": "^0.49.0",
"ember-auto-import": "^2.2.0",
"semver": "^7.3.5"
},
"devDependencies": {
Expand All @@ -33,9 +30,6 @@
"engines": {
"node": "12.* || 14.* || >= 16"
},
"ember-addon": {
"main": "addon-main.js"
},
"volta": {
"extends": "../../package.json"
}
Expand Down
91 changes: 70 additions & 21 deletions packages/addon-shim/src/index.ts
Expand Up @@ -40,45 +40,49 @@ export function addonV1Shim(directory: string, options: ShimOptions = {}) {
return tree;
}

let autoImportInstance: EAI2Instance | undefined;

return {
name: pkg.name,
included(this: AddonInstance, ...args: unknown[]) {
if ((this.parent.pkg['ember-addon']?.version ?? 1) < 2) {
let autoImportVersion = this.parent.addons.find(
(a) => a.name === 'ember-auto-import'
)?.pkg.version;
let parentOptions;
let parentName: string;
if (isDeepAddonInstance(this)) {
parentOptions = this.parent.options;
parentName = this.parent.name;
} else {
parentOptions = this.app.options;
parentName = this.parent.name();
}

if (!autoImportVersion) {
// if we're being used by a v1 package, that package needs ember-auto-import 2
if ((this.parent.pkg['ember-addon']?.version ?? 1) < 2) {
let autoImport = locateAutoImport(this.parent.addons);
if (!autoImport.present) {
throw new Error(
`${this.parent.name} needs to depend on ember-auto-import in order to use ${this.name}`
`${parentName} needs to depend on ember-auto-import in order to use ${this.name}`
);
}

if (
!satisfies(autoImportVersion, '>=2.0.0-alpha.0', {
includePrerelease: true,
})
) {
if (!autoImport.satisfiesV2) {
throw new Error(
`${this.parent.name} has ember-auto-import ${autoImportVersion} which is not new enough to use ${this.name}. It needs to upgrade to >=2.0`
`${parentName} has ember-auto-import ${autoImport.version} which is not new enough to use ${this.name}. It needs to upgrade to >=2.0`
);
}
}

let parentOptions;
if (isDeepAddonInstance(this)) {
parentOptions = this.parent.options;
autoImportInstance = autoImport.instance;
autoImportInstance.registerV2Addon(this.name, directory);
} else {
parentOptions = this.app.options;
// if we're being used by a v2 addon, it also has this shim and will
// forward our registration onward to ember-auto-import
(this.parent as EAI2Instance).registerV2Addon(this.name, directory);
}

if (options.disabled) {
disabled = options.disabled(parentOptions);
}

// this is here so that our possible exceptions above take precedence over
// the one that ember-auto-import will also throw if the app doesn't have
// ember-auto-import
// this is at the end so we can find our own autoImportInstance before any
// deeper v2 addons ask us to forward registrations upward to it
this._super.included.apply(this, args);
},

Expand Down Expand Up @@ -153,10 +157,55 @@ export function addonV1Shim(directory: string, options: ShimOptions = {}) {
let appInstance = this._findHost();
return isInside(directory, appInstance.project.root);
},

registerV2Addon(name: string, root: string): void {
autoImportInstance!.registerV2Addon(name, root);
},
};
}

function isInside(parentDir: string, otherDir: string): boolean {
let rel = relative(parentDir, otherDir);
return Boolean(rel) && !rel.startsWith('..') && !isAbsolute(rel);
}

type EAI2Instance = AddonInstance & {
registerV2Addon(name: string, root: string): void;
};

function locateAutoImport(addons: AddonInstance[]):
| { present: false }
| {
present: true;
version: string;
satisfiesV2: false;
}
| {
present: true;
version: string;
satisfiesV2: true;
instance: EAI2Instance;
} {
let instance = addons.find((a) => a.name === 'ember-auto-import');
if (!instance) {
return { present: false };
}
let version = instance.pkg.version;
let satisfiesV2 = satisfies(version, '>=2.0.0-alpha.0', {
includePrerelease: true,
});
if (satisfiesV2) {
return {
present: true,
version,
satisfiesV2,
instance: instance as EAI2Instance,
};
} else {
return {
present: true,
version,
satisfiesV2,
};
}
}
45 changes: 26 additions & 19 deletions packages/core/src/babel-plugin-adjust-imports.ts
Expand Up @@ -231,6 +231,27 @@ function handleExternal(specifier: string, sourceFile: AdjustFile, opts: Options
return makeExternal(specifier, sourceFile, opts);
}

if (!pkg.meta['auto-upgraded'] && emberVirtualPeerDeps.has(packageName)) {
// Native v2 addons are allowed to use the emberVirtualPeerDeps like
// `@glimmer/component`. And like all v2 addons, it's important that they
// see those dependencies after those dependencies have been converted to
// v2.
//
// But unlike auto-upgraded addons, native v2 addons are not necessarily
// copied out of their original place in node_modules. And from that
// original place they might accidentally resolve the emberVirtualPeerDeps
// that are present there in v1 format.
//
// So before we even check isResolvable, we adjust these imports to point at
// the app's copies instead.
if (emberVirtualPeerDeps.has(packageName)) {
if (!opts.activeAddons[packageName]) {
throw new Error(`${pkg.name} is trying to import the app's ${packageName} package, but it seems to be missing`);
}
return explicitRelative(dirname(sourceFile.name), specifier.replace(packageName, opts.activeAddons[packageName]));
}
}

let relocatedPkg = sourceFile.relocatedIntoPackage();
if (relocatedPkg) {
// this file has been moved into another package (presumably the app).
Expand Down Expand Up @@ -278,33 +299,19 @@ function handleExternal(specifier: string, sourceFile: AdjustFile, opts: Options
return explicitRelative(dirname(sourceFile.name), specifier.replace(packageName, opts.activeAddons[packageName]));
}

// auto-upgraded packages can fall back to attmpeting to find dependencies at
// runtime. Native v2 packages can only get this behavior in the
// isExplicitlyExternal case above because they need to explicitly ask for
// externals.
if (pkg.meta['auto-upgraded']) {
// auto-upgraded packages can fall back to attempting to find dependencies at
// runtime. Native v2 packages can only get this behavior in the
// isExplicitlyExternal case above because they need to explicitly ask for
// externals.
return makeExternal(specifier, sourceFile, opts);
}

if (pkg.isV2Ember()) {
} else {
// native v2 packages don't automatically externalize *everything* the way
// auto-upgraded packages do, but they still externalize known and approved
// ember virtual packages (like @ember/component)
if (emberVirtualPackages.has(packageName)) {
return makeExternal(specifier, sourceFile, opts);
}

// native v2 packages don't automatically get to use every other addon as a
// peerDep, but they do get the known and approved ember virtual peer deps,
// like @glimmer/component
if (emberVirtualPeerDeps.has(packageName)) {
if (!opts.activeAddons[packageName]) {
throw new Error(
`${pkg.name} is trying to import from ${packageName}, which is supposed to be present in all ember apps but seems to be missing`
);
}
return explicitRelative(dirname(sourceFile.name), specifier.replace(packageName, opts.activeAddons[packageName]));
}
}

// non-resolvable imports in dynamic positions become runtime errors, not
Expand Down
2 changes: 1 addition & 1 deletion packages/shared-internals/src/ember-standard-modules.ts
Expand Up @@ -17,7 +17,7 @@ import mappings from 'ember-rfc176-data/mappings.json';
// map them into real modules within ember-source.
export const emberVirtualPackages = new Set<string>(mappings.map((m: any) => m.module));

// these are *real* packages that every ember addon is allow to resolve *as if
// these are *real* packages that every ember addon is allowed to resolve *as if
// they were peerDepenedencies, because the host application promises to have
// these packages. In principle, we could force every addon to declare these as
// real peerDeps all the way down the dependency graph, but in practice that
Expand Down

0 comments on commit 30373fd

Please sign in to comment.