Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

clarify which package rules apply inside vs outside a component #1362

Merged
merged 1 commit into from
Feb 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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