Skip to content

Commit

Permalink
Merge pull request #1362 from embroider-build/rule-separation
Browse files Browse the repository at this point in the history
clarify which package rules apply inside vs outside a component
  • Loading branch information
ef4 committed Feb 15, 2023
2 parents e05dfdc + e806cc7 commit 07818cc
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 45 deletions.
35 changes: 24 additions & 11 deletions packages/compat/src/dependency-rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,11 +152,19 @@ export type ArgumentMapping =
type ComponentSnippet = string;

export interface PreprocessedComponentRule {
yieldsSafeComponents: Required<ComponentRules>['yieldsSafeComponents'];
yieldsArguments: Required<ComponentRules>['yieldsArguments'];
dependsOnComponents: ComponentSnippet[];
argumentsAreComponents: string[];
safeInteriorPaths: string[];
exterior: {
// rules needed by the people that invoke our component
yieldsSafeComponents: Required<ComponentRules>['yieldsSafeComponents'];
yieldsArguments: Required<ComponentRules>['yieldsArguments'];
argumentsAreComponents: string[];
safeToIgnore: boolean;
};

interior: {
// rules needed within our own template)
dependsOnComponents: ComponentSnippet[];
safeInteriorPaths: string[];
};
}

// take a component rule from the authoring format to a format more optimized
Expand Down Expand Up @@ -190,11 +198,16 @@ export function preprocessComponentRule(componentRules: ComponentRules): Preproc
}
}
return {
argumentsAreComponents,
safeInteriorPaths,
dependsOnComponents,
yieldsSafeComponents: componentRules.yieldsSafeComponents || [],
yieldsArguments: componentRules.yieldsArguments || [],
interior: {
safeInteriorPaths,
dependsOnComponents,
},
exterior: {
safeToIgnore: Boolean(componentRules.safeToIgnore),
argumentsAreComponents,
yieldsSafeComponents: componentRules.yieldsSafeComponents || [],
yieldsArguments: componentRules.yieldsArguments || [],
},
};
}

Expand Down Expand Up @@ -235,7 +248,7 @@ export function expandModuleRules(absPath: string, moduleRules: ModuleRules, res
}
if (moduleRules.dependsOnComponents) {
for (let snippet of moduleRules.dependsOnComponents) {
let found = resolver.resolveComponentSnippet(snippet, moduleRules);
let found = resolver.resolveComponentSnippet(snippet, moduleRules, absPath);
if (found.jsModule) {
let { absPath: target, runtimeName } = found.jsModule;
output.push({ absPath, target: explicitRelative(dirname(absPath), target), runtimeName });
Expand Down
56 changes: 27 additions & 29 deletions packages/compat/src/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ export default class CompatResolver {
}
}
enter(moduleName: string) {
let rules = this.findComponentRules(moduleName);
let rules = this.findInteriorRules(moduleName);
let deps: ComponentResolution[];
if (rules?.dependsOnComponents) {
deps = rules.dependsOnComponents.map(snippet => this.resolveComponentSnippet(snippet, rules!, moduleName));
Expand All @@ -170,8 +170,8 @@ export default class CompatResolver {
}
return deps;
}
private findComponentRules(absPath: string): PreprocessedComponentRule | undefined {
let rules = this.rules.components.get(absPath);
private findInteriorRules(absPath: string): PreprocessedComponentRule['interior'] | undefined {
let rules = this.rules.interiorRules.get(absPath);
if (rules) {
return rules;
}
Expand All @@ -185,7 +185,7 @@ export default class CompatResolver {
let stem = absPath.slice(0, -4);
for (let ext of this.params.resolvableExtensions) {
if (ext !== '.hbs') {
let rules = this.rules.components.get(stem + ext);
let rules = this.rules.interiorRules.get(stem + ext);
if (rules) {
return rules;
}
Expand All @@ -196,46 +196,51 @@ export default class CompatResolver {
}

private isIgnoredComponent(dasherizedName: string) {
return this.rules.ignoredComponents.includes(dasherizedName);
return this.rules.exteriorRules.get(dasherizedName)?.safeToIgnore;
}

@Memoize()
private get rules() {
// keyed by their first resolved dependency's absPath.
let components: Map<string, PreprocessedComponentRule> = new Map();
let interiorRules: Map<string, PreprocessedComponentRule['interior']> = new Map();

// keyed by our own dasherized interpretation of the component's name.
let ignoredComponents: string[] = [];
// keyed by our dasherized interpretation of the component's name
let exteriorRules: Map<string, PreprocessedComponentRule['exterior']> = new Map();

// we're not responsible for filtering out rules for inactive packages here,
// that is done before getting to us. So we should assume these are all in
// force.
for (let rule of this.params.activePackageRules) {
if (rule.components) {
for (let [snippet, componentRules] of Object.entries(rule.components)) {
if (componentRules.safeToIgnore) {
ignoredComponents.push(this.standardDasherize(snippet, rule));
let processedRules = preprocessComponentRule(componentRules);
let dasherizedName = this.standardDasherize(snippet, rule);
exteriorRules.set(dasherizedName, processedRules.exterior);
if (processedRules.exterior.safeToIgnore) {
continue;
}
let resolvedSnippet = this.resolveComponentSnippet(snippet, rule);

let resolvedSnippet = this.resolveComponentSnippet(
snippet,
rule,
pathResolve(this.params.appRoot, 'package.json')
);

// cast is OK here because a component must have one or the other
let resolvedDep = (resolvedSnippet.hbsModule ?? resolvedSnippet.jsModule)!;

let processedRules = preprocessComponentRule(componentRules);

// we always register our rules on the component's own first resolved
// module, which must be a module in the app's module namespace.
components.set(resolvedDep.absPath, processedRules);
interiorRules.set(resolvedDep.absPath, processedRules.interior);

// if there's a custom layout, we also need to register our rules on
// those templates.
if (componentRules.layout) {
if (componentRules.layout.appPath) {
components.set(join(this.params.appRoot, componentRules.layout.appPath), processedRules);
interiorRules.set(join(this.params.appRoot, componentRules.layout.appPath), processedRules.interior);
} else if (componentRules.layout.addonPath) {
for (let root of rule.roots) {
components.set(join(root, componentRules.layout.addonPath), processedRules);
interiorRules.set(join(root, componentRules.layout.addonPath), processedRules.interior);
}
} else {
throw new Error(
Expand All @@ -252,26 +257,22 @@ export default class CompatResolver {
if (rule.appTemplates) {
for (let [path, templateRules] of Object.entries(rule.appTemplates)) {
let processedRules = preprocessComponentRule(templateRules);
components.set(join(this.params.appRoot, path), processedRules);
interiorRules.set(join(this.params.appRoot, path), processedRules.interior);
}
}
if (rule.addonTemplates) {
for (let [path, templateRules] of Object.entries(rule.addonTemplates)) {
let processedRules = preprocessComponentRule(templateRules);
for (let root of rule.roots) {
components.set(join(root, path), processedRules);
interiorRules.set(join(root, path), processedRules.interior);
}
}
}
}
return { components, ignoredComponents };
return { interiorRules, exteriorRules };
}

resolveComponentSnippet(
snippet: string,
rule: PackageRules | ModuleRules,
from = 'rule-snippet.hbs'
): ComponentResolution {
resolveComponentSnippet(snippet: string, rule: PackageRules | ModuleRules, from: string): ComponentResolution {
let name = this.standardDasherize(snippet, rule);
let found = this.tryComponent(name, from, false);
if (found && found.type === 'component') {
Expand Down Expand Up @@ -484,10 +485,7 @@ export default class CompatResolver {

let componentRules;
if (withRuleLookup) {
// the order here is important. We follow the convention that any rules
// get attached to the hbsModule if it exists, and only get attached to
// the jsModule otherwise
componentRules = this.findComponentRules((hbsModule ?? jsModule)!.absPath);
componentRules = this.rules.exteriorRules.get(path);
}
return {
type: 'component',
Expand Down Expand Up @@ -635,7 +633,7 @@ export default class CompatResolver {
};
}
if (component.type === 'path') {
let ownComponentRules = this.findComponentRules(from);
let ownComponentRules = this.findInteriorRules(from);
if (ownComponentRules && ownComponentRules.safeInteriorPaths.includes(component.path)) {
return null;
}
Expand Down
5 changes: 0 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -16130,11 +16130,6 @@ webpack-sources@^3.2.3:
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==

webpack-virtual-modules@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/webpack-virtual-modules/-/webpack-virtual-modules-0.5.0.tgz#362f14738a56dae107937ab98ea7062e8bdd3b6c"
integrity sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==

webpack@^5, webpack@^5.38.1, webpack@^5.72.1, webpack@^5.74.0:
version "5.75.0"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.75.0.tgz#1e440468647b2505860e94c9ff3e44d5b582c152"
Expand Down

0 comments on commit 07818cc

Please sign in to comment.