ember-auto-import
Version:
Zero-config import from NPM packages
285 lines • 13.3 kB
JavaScript
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const splitter_1 = __importDefault(require("./splitter"));
const bundler_1 = require("./bundler");
const analyzer_1 = __importDefault(require("./analyzer"));
const package_1 = __importDefault(require("./package"));
const broccoli_debug_1 = __importDefault(require("broccoli-debug"));
const bundle_config_1 = __importDefault(require("./bundle-config"));
const leader_1 = require("./leader");
const shared_internals_1 = require("@embroider/shared-internals");
const webpack_1 = __importDefault(require("./webpack"));
const typescript_memoize_1 = require("typescript-memoize");
const broccoli_source_1 = require("broccoli-source");
const inserter_1 = require("./inserter");
const broccoli_merge_trees_1 = __importDefault(require("broccoli-merge-trees"));
const resolve_1 = __importDefault(require("resolve"));
const resolve_package_path_1 = __importDefault(require("resolve-package-path"));
const semver_1 = __importDefault(require("semver"));
const analyzer_syntax_1 = require("./analyzer-syntax");
const path_1 = __importDefault(require("path"));
const broccoli_funnel_1 = __importDefault(require("broccoli-funnel"));
const debug_1 = __importDefault(require("debug"));
const reverse_exports_1 = require("@embroider/reverse-exports");
const debugTree = broccoli_debug_1.default.buildDebugCallback('ember-auto-import');
const debugWatch = (0, debug_1.default)('ember-auto-import:watch');
class AutoImport {
static register(addon) {
leader_1.LeaderChooser.for(addon).register(addon, () => new AutoImport(addon));
}
static lookup(addon) {
return leader_1.LeaderChooser.for(addon).leader;
}
constructor(addonInstance) {
this.packages = new Set();
this.analyzers = new Map();
// maps packageName to packageRoot
this.v2Addons = new Map();
let topmostAddon = (0, shared_internals_1.findTopmostAddon)(addonInstance);
this.packageCache = shared_internals_1.PackageCache.shared('ember-auto-import', topmostAddon.project.root);
this.packages.add(package_1.default.lookupParentOf(topmostAddon, this.v2AddonResolver));
let host = topmostAddon.app;
this.installAppFilter(host);
this.env = host.env;
this.bundles = new bundle_config_1.default(host.options.outputPaths);
if (!this.env) {
throw new Error('Bug in ember-auto-import: did not discover environment');
}
this.consoleWrite = (...args) => addonInstance.project.ui.write(...args);
}
installAppFilter(_host) {
// TODO upstream this type change to @embroider/shared-internals
let host = _host;
if (this.rootPackage.allowAppImports.length) {
host.trees.app = (0, broccoli_funnel_1.default)(host.trees.app, {
exclude: this.rootPackage.allowAppImports,
});
}
}
// we don't actually call this ourselves anymore, but earlier versions of
// ember-auto-import will still call it on us. For them the answer is always
// false.
isPrimary(_addon) {
return false;
}
analyze(tree, addon, treeType, supportsFastAnalyzer) {
let pack = package_1.default.lookupParentOf(addon, this.v2AddonResolver);
this.packages.add(pack);
let analyzer = new analyzer_1.default(debugTree(tree, `preprocessor:input-${this.analyzers.size}`), pack, treeType, supportsFastAnalyzer);
this.analyzers.set(analyzer, pack);
return analyzer;
}
registerV2Addon(packageName, packageRoot, options = {}) {
this.v2Addons.set(packageName, { root: packageRoot, options });
}
get v2AddonResolver() {
return {
hasV2Addon: (name) => {
return this.v2Addons.has(name);
},
v2AddonRoot: (name) => {
var _a;
return (_a = this.v2Addons.get(name)) === null || _a === void 0 ? void 0 : _a.root;
},
handleRenaming: (name) => {
let hit = this.renamedModules().get(name);
if (hit) {
return hit;
}
hit = this.renamedModules().get(name + '.js');
if (hit) {
return hit;
}
hit = this.renamedModules().get(name + '/index.js');
if (hit) {
return hit;
}
return name;
},
implicitImports: (kind, packageRoot) => {
var _a, _b;
let output = [];
for (let dep of this.packageCache.get(packageRoot).dependencies) {
if (dep.isV2Addon()) {
let meta = dep.meta;
let customize = (_b = (_a = this.v2Addons.get(dep.name)) === null || _a === void 0 ? void 0 : _a.options) === null || _b === void 0 ? void 0 : _b.customizeMeta;
if (customize) {
// json here is just a super simple clone so the hook can't mutate
// our cache unintentionally
meta = customize(JSON.parse(JSON.stringify(meta)));
}
let implicitModules = meta[kind];
if (implicitModules) {
for (let localPath of implicitModules) {
let specifier = (0, reverse_exports_1.externalName)(dep.packageJSON, localPath);
if (!specifier) {
throw new Error(`${dep.name} declared implicit-module ${localPath} but that is not accessible outside the package`);
}
if (meta['renamed-modules']) {
for (let [renamed, original] of Object.entries(meta['renamed-modules'])) {
if (specifier === original) {
specifier = renamed;
}
}
}
if (specifier.endsWith('.js')) {
specifier = specifier.slice(0, -3);
}
output.push(specifier);
}
}
}
}
return output;
},
};
}
renamedModules() {
if (!this._renamedModules) {
this._renamedModules = new Map();
for (let { root, options } of this.v2Addons.values()) {
let pkg = this.packageCache.get(root);
if (pkg.isV2Addon()) {
let meta = pkg.meta;
if (options.customizeMeta) {
// json here is just a super simple clone so the hook can't mutate
// our cache unintentionally
meta = options.customizeMeta(JSON.parse(JSON.stringify(meta)));
}
let renamedModules = meta['renamed-modules'];
if (renamedModules) {
for (let [from, to] of Object.entries(renamedModules)) {
this._renamedModules.set(from, to);
}
}
}
}
}
return this._renamedModules;
}
makeBundler(allAppTree) {
// The Splitter takes the set of imports from the Analyzer and
// decides which ones to include in which bundles
let splitter = new splitter_1.default({
analyzers: this.analyzers,
bundles: this.bundles,
});
let webpack;
const pkg = (0, resolve_package_path_1.default)('webpack', this.rootPackage.root);
// eslint-disable-next-line @typescript-eslint/no-var-requires
if (pkg && semver_1.default.satisfies(require(pkg).version, '^5.0.0')) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
webpack = require(resolve_1.default.sync('webpack', {
basedir: this.rootPackage.root,
}));
}
else {
throw new Error(`[ember-auto-import] this version of ember-auto-import requires the app to have a dependency on webpack 5`);
}
// The Bundler asks the splitter for deps it should include and
// is responsible for packaging those deps up.
return new webpack_1.default(depsFor(allAppTree, this.packages), {
splitter,
environment: this.env,
packages: this.packages,
consoleWrite: this.consoleWrite,
bundles: this.bundles,
webpack,
rootPackage: this.rootPackage,
v2AddonResolver: this.v2AddonResolver,
});
}
get rootPackage() {
let rootPackage = [...this.packages.values()].find((pkg) => !pkg.isAddon);
if (!rootPackage) {
throw new Error(`bug in ember-auto-import, there should always be a Package representing the app`);
}
return rootPackage;
}
addTo(allAppTree) {
let bundler = (0, bundler_1.debugBundler)(this.makeBundler(allAppTree), 'output');
let inserter = new inserter_1.Inserter(allAppTree, bundler, this.bundles, {
publicAssetURL: this.rootPackage.publicAssetURL(),
insertScriptsAt: this.rootPackage.insertScriptsAt,
insertStylesAt: this.rootPackage.insertStylesAt,
});
let trees = [allAppTree, bundler, inserter];
return (0, broccoli_merge_trees_1.default)(trees, { overwrite: true });
}
// CAUTION: versions <= 2.1.0 only invoked this method on the app's copy of
// ember-auto-import, whereas we now invoke it on every copy. That means you
// can't guarantee this will be called for an addon that is using one of those
// older versions.
included(addonInstance) {
this.installBabelPlugin(addonInstance);
if (!(0, shared_internals_1.isDeepAddonInstance)(addonInstance)) {
this.configureFingerprints(addonInstance.app);
}
}
installBabelPlugin(addonInstance) {
let parent;
if ((0, shared_internals_1.isDeepAddonInstance)(addonInstance)) {
parent = addonInstance.parent;
}
else {
parent = addonInstance.app;
}
let babelOptions = (parent.options.babel =
parent.options.babel || {});
let babelPlugins = (babelOptions.plugins = babelOptions.plugins || []);
if (!babelPlugins.some(isAnalyzerPlugin)) {
// the MARKER is included so that babel caches will invalidate if the
// MARKER changes
babelPlugins.unshift([require.resolve('./analyzer-plugin'), { MARKER: analyzer_syntax_1.MARKER }]);
}
}
// We need to disable fingerprinting of chunks, because (1) they already
// have their own webpack-generated hashes and (2) the runtime loader code
// can't easily be told about broccoli-asset-rev's hashes.
configureFingerprints(host) {
let patterns = ['assets/chunk.*.js', 'assets/chunk.*.css'];
if (!host.options.fingerprint) {
host.options.fingerprint = {};
}
if (!('exclude' in host.options.fingerprint)) {
host.options.fingerprint.exclude = patterns;
}
else {
for (let pattern of patterns) {
host.options.fingerprint.exclude.push(pattern);
}
}
}
}
exports.default = AutoImport;
__decorate([
(0, typescript_memoize_1.Memoize)()
], AutoImport.prototype, "rootPackage", null);
function depsFor(allAppTree, packages) {
let deps = [allAppTree];
for (let pkg of packages) {
let watched = pkg.watchedDirectories;
if (watched) {
deps = deps.concat(watched.map((dir) => new broccoli_source_1.WatchedDir(dir)));
debugWatch(`Adding watched directories: ${watched.join(', ')}`);
}
}
return deps;
}
function isAnalyzerPlugin(entry) {
const suffix = path_1.default.join('ember-auto-import', 'js', 'analyzer-plugin.js');
return ((typeof entry === 'string' && entry.endsWith(suffix)) ||
(Array.isArray(entry) &&
typeof entry[0] === 'string' &&
entry[0].endsWith(suffix)));
}
//# sourceMappingURL=auto-import.js.map