Skip to content

Commit

Permalink
Merge pull request #732 from embroider-build/v2-shim
Browse files Browse the repository at this point in the history
v2 addon shim
  • Loading branch information
ef4 authored Mar 30, 2021
2 parents 031ce6d + 451f780 commit c375da3
Show file tree
Hide file tree
Showing 20 changed files with 377 additions and 35 deletions.
4 changes: 4 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
/packages/compat/**/*.d.ts
/packages/macros/**/*.js
/packages/macros/**/*.d.ts
/packages/util/src/**/*.js
/packages/util/src/**/*.d.ts
/packages/util/shim.js
/packages/util/shim.d.ts
/packages/webpack/**/*.js
/packages/webpack/**/*.d.ts
/packages/hbs-loader/**/*.js
Expand Down
2 changes: 1 addition & 1 deletion packages/compat/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"broccoli": "^3.4.2",
"broccoli-concat": "^3.7.3",
"broccoli-file-creator": "^2.1.1",
"broccoli-funnel": "^2.0.1",
"broccoli-funnel": "ef4/broccoli-funnel#c70d060076e14793e8495571f304a976afc754ac",
"broccoli-merge-trees": "^3.0.0",
"broccoli-persistent-filter": "^3.1.2",
"broccoli-plugin": "^4.0.0",
Expand Down
23 changes: 15 additions & 8 deletions packages/compat/src/compat-addons.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Node } from 'broccoli-node-api';
import { join, relative, dirname } from 'path';
import { join, relative, dirname, isAbsolute } from 'path';
import { emptyDirSync, ensureSymlinkSync, ensureDirSync, realpathSync, copySync, writeJSONSync } from 'fs-extra';
import { Stage, Package, PackageCache, WaitForTrees, mangledEngineRoot } from '@embroider/core';
import V1InstanceCache from './v1-instance-cache';
Expand Down Expand Up @@ -111,14 +111,21 @@ export default class CompatAddons implements Stage {

if (!treeInstance) {
let ignore = ['**/node_modules'];
if (join(destination, 'tests', 'dummy') === this.appDestDir) {
// special case: we're building the dummy app of this addon. Because
// the dummy app is nested underneath the addon, we need to tell our
// TreeSync to ignore it. Not because it's ever present at our input,
// but because stage2 will make it appear inside our output and we
// should leave that alone.
ignore.push('tests');

let rel = relative(destination, this.appDestDir);
if (!rel.startsWith('..') && !isAbsolute(rel)) {
// the app is inside our addon. We must not copy the app as part of
// the addon, because that would overwrite the real app build.
ignore.push(rel);

if (rel === 'tests/dummy') {
// special case: classic dummy apps are weird because they put the
// tests (which are truly part of the app, not the addon) inside the
// addon instead of inside the app.
ignore.push('tests');
}
}

treeInstance = new TreeSync(movedAddons[index], destination, {
ignore,
});
Expand Down
29 changes: 28 additions & 1 deletion packages/core/src/babel-plugin-adjust-imports.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import getPackageName from './package-name';
import { emberVirtualPackages, emberVirtualPeerDeps, packageName as getPackageName } from '@embroider/shared-internals';
import { join, dirname, resolve } from 'path';
import { NodePath } from '@babel/traverse';
import type * as t from '@babel/types';
Expand Down Expand Up @@ -227,6 +227,12 @@ function handleExternal(specifier: string, sourceFile: AdjustFile, opts: Options
let relocatedPkg = sourceFile.relocatedIntoPackage();
if (relocatedPkg) {
// this file has been moved into another package (presumably the app).

// self-imports are legal in the app tree, even for v2 packages
if (packageName === pkg.name) {
return specifier;
}

// first try to resolve from the destination package
if (isResolvable(packageName, relocatedPkg)) {
if (!pkg.meta['auto-upgraded']) {
Expand Down Expand Up @@ -273,6 +279,27 @@ function handleExternal(specifier: string, sourceFile: AdjustFile, opts: Options
return makeExternal(specifier, sourceFile, opts);
}

if (pkg.isV2Ember()) {
// 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
// build-time errors, so we emit the runtime error module here before the
// stage3 packager has a chance to see the missing module. (Maybe some stage3
Expand Down
17 changes: 3 additions & 14 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,13 @@ export { Plugins as TemplateCompilerPlugins } from './ember-template-compiler-ty
export { Asset, EmberAsset, ImplicitAssetPaths } from './asset';
export { default as Options, optionsWithDefaults } from './options';
export { default as toBroccoliPlugin } from './to-broccoli-plugin';
export { default as packageName } from './package-name';
export { default as WaitForTrees, OutputPaths } from './wait-for-trees';
export { default as BuildStage } from './build-stage';
export { compile as jsHandlebarsCompile } from './js-handlebars';
export { AppAdapter, AppBuilder, EmberENV } from './app';
export { todo, unsupported, warn, debug, expectWarning, throwOnWarnings } from './messages';
export { mangledEngineRoot } from './engine-mangler';

export {
AppMeta,
AddonMeta,
explicitRelative,
extensionsPattern,
getOrCreate,
Package,
AddonPackage,
AppPackage,
V2Package,
PackageCache,
babelFilter,
} from '@embroider/shared-internals';
// this is reexported because we already make users manage a peerDep from some
// other packages (like embroider/webpack and @embroider/
export * from '@embroider/shared-internals';
1 change: 1 addition & 0 deletions packages/shared-internals/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"test": "jest"
},
"dependencies": {
"ember-rfc176-data": "^0.3.17",
"resolve-package-path": "^1.2.2",
"pkg-up": "^3.1.0",
"typescript-memoize": "^1.0.0-alpha.3",
Expand Down
55 changes: 55 additions & 0 deletions packages/shared-internals/src/ember-cli-models.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import type { Node } from 'broccoli-node-api';
export interface Project {
targets: unknown;
ui: {
write(...args: any[]): void;
};
pkg: { name: string; version: string };
root: string;
addons: AddonInstance[];
name(): string;
}

export interface AppInstance {
env: 'development' | 'test' | 'production';
project: Project;
options: any;
addonPostprocessTree: (which: string, tree: Node) => Node;
}

interface BaseAddonInstance {
project: Project;
pkg: { name: string; version: string };
root: string;
options: any;
addons: AddonInstance[];
name: string;
_super: any;
treeGenerator(path: string): Node;
_findHost(): AppInstance;
}

export interface DeepAddonInstance extends BaseAddonInstance {
// this is how it looks when an addon is beneath another addon
parent: AddonInstance;
}

export interface ShallowAddonInstance extends BaseAddonInstance {
// this is how it looks when an addon is directly beneath the app
parent: Project;
app: AppInstance;
}

export type AddonInstance = DeepAddonInstance | ShallowAddonInstance;

export function isDeepAddonInstance(addon: AddonInstance): addon is DeepAddonInstance {
return addon.parent !== addon.project;
}

export function findTopmostAddon(addon: AddonInstance): ShallowAddonInstance {
if (isDeepAddonInstance(addon)) {
return findTopmostAddon(addon.parent);
} else {
return addon;
}
}
27 changes: 27 additions & 0 deletions packages/shared-internals/src/ember-standard-modules.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// I'm doing this as a json import because even though that's not standard JS,
// it's relaively easy to consume into builds for the web. As opposed to doing
// something like fs.readFile, which is harder.
//
// @ts-ignore
import mappings from 'ember-rfc176-data/mappings.json';

// these are packages that available to import in standard ember code that don't
// exist as real packages. If a build system encounters them in stage 3, it
// should convert them to runtime AMD require.
//
// Some of them (like @embroider/macros) won't ever be seen in stage 3, because
// earlier plugins should take care of them.
//
// In embroider builds using ember-source >= 3.28, you won't see *any* of these
// in stage3 because ember-source uses the standard rename-modules feature to
// 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
// 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
// makes the migration from v1 to v2 addons more painful than necessary, because
// a v1 addon in between the app and a v2 addon might not declare the peerDep,
// breaking the deeper v2 addon.
export const emberVirtualPeerDeps = new Set<string>(['@glimmer/component']);
3 changes: 3 additions & 0 deletions packages/shared-internals/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ export { getOrCreate } from './get-or-create';
export { default as Package, V2AddonPackage as AddonPackage, V2AppPackage as AppPackage, V2Package } from './package';
export { default as PackageCache } from './package-cache';
export { default as babelFilter } from './babel-filter';
export { default as packageName } from './package-name';
export * from './ember-cli-models';
export * from './ember-standard-modules';
File renamed without changes.
4 changes: 4 additions & 0 deletions packages/util/.eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
# compiled output
/dist/
/tmp/
/src/**/*.js
/src/**/*.d.ts
/shim.js
/shim.d.ts

# dependencies
/bower_components/
Expand Down
30 changes: 29 additions & 1 deletion packages/util/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ module.exports = {
'.prettierrc.js',
'.template-lintrc.js',
'ember-cli-build.js',
'index.js',
'addon-main.js',
'testem.js',
'blueprints/*/index.js',
'config/**/*.js',
Expand All @@ -49,5 +49,33 @@ module.exports = {
plugins: ['node'],
extends: ['plugin:node/recommended'],
},
// node typescript files
{
files: ['src/**/*.ts', 'shim.ts'],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 2017,
sourceType: 'module',
},
plugins: ['@typescript-eslint'],
extends: ['prettier/@typescript-eslint'],
rules: {
'@typescript-eslint/naming-convention': [
'error',
{
selector: 'typeLike',
format: ['PascalCase'],
},
],
'@typescript-eslint/consistent-type-assertions': 'error',
'@typescript-eslint/no-inferrable-types': 'error',
'@typescript-eslint/no-unused-vars': [
'error',
{ argsIgnorePattern: '^_' },
],
'no-unused-vars': 'off',
'@typescript-eslint/no-require-imports': 'error',
},
},
],
};
6 changes: 6 additions & 0 deletions packages/util/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
# compiled output
/dist/
/tmp/
/src/**/*.js
/src/**/*.d.ts
/src/**/*.map
/shim.js
/shim.d.ts
/shim.js.map

# dependencies
/bower_components/
Expand Down
File renamed without changes.
1 change: 0 additions & 1 deletion packages/util/index.d.ts

This file was deleted.

7 changes: 6 additions & 1 deletion packages/util/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"test": "tests"
},
"scripts": {
"prepare": "tsc",
"build": "ember build --environment=production",
"lint": "npm-run-all --aggregate-output --continue-on-error --parallel 'lint:!(fix)'",
"lint:fix": "npm-run-all --aggregate-output --continue-on-error --parallel lint:*:fix",
Expand All @@ -28,6 +29,7 @@
},
"dependencies": {
"@embroider/macros": "0.37.0",
"broccoli-funnel": "ef4/broccoli-funnel#c70d060076e14793e8495571f304a976afc754ac",
"ember-cli-babel": "^7.23.1"
},
"devDependencies": {
Expand All @@ -40,6 +42,8 @@
"@embroider/webpack": "0.37.0",
"@glimmer/component": "^1.0.3",
"@glimmer/tracking": "^1.0.3",
"@typescript-eslint/eslint-plugin": "^4.1.1",
"@typescript-eslint/parser": "^4.1.1",
"babel-eslint": "^10.1.0",
"broccoli-asset-rev": "^3.0.0",
"ember-auto-import": "^1.10.1",
Expand Down Expand Up @@ -78,7 +82,8 @@
"edition": "octane"
},
"ember-addon": {
"configPath": "tests/dummy/config"
"configPath": "tests/dummy/config",
"main": "addon-main.js"
},
"volta": {
"extends": "../../package.json"
Expand Down
Loading

0 comments on commit c375da3

Please sign in to comment.