diff --git a/package.json b/package.json index 3049882..72e28df 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "cosmiconfig": "^8.1.3", "deepmerge-ts": "^4.3.0", "ember-codemods-telemetry-helpers": "^3.0.0", + "glob": "^8.1.0", "minimatch": "^7.4.6", "winston": "^3.8.2", "zod": "^3.21.4" @@ -75,7 +76,6 @@ "eslint-plugin-node": "^11.1.0", "eslint-plugin-unicorn": "^46.0.0", "execa": "^5.1.1", - "glob": "^8.1.0", "jest": "^29.5.0", "prettier": "^2.8.8", "release-it": "^15.10.1", diff --git a/transforms/helpers/options.ts b/transforms/helpers/options.ts index 24c15ff..0752b0d 100644 --- a/transforms/helpers/options.ts +++ b/transforms/helpers/options.ts @@ -100,6 +100,8 @@ export const UserOptionsSchema = z.object({ type: TypeSchema.describe( 'Apply transformation to only passed type.' ).optional(), + moduleRoot: z.string().min(1).optional().describe('FIXME'), + packageBase: z.string().min(1).optional().describe('FIXME'), }); export type UserOptions = z.infer; diff --git a/transforms/helpers/runtime-data.ts b/transforms/helpers/runtime-data.ts index e34dc69..14f07a5 100644 --- a/transforms/helpers/runtime-data.ts +++ b/transforms/helpers/runtime-data.ts @@ -1,7 +1,12 @@ -import { getTelemetryFor } from 'ember-codemods-telemetry-helpers'; -import path from 'path'; +import { + getTelemetry, + getTelemetryFor, +} from 'ember-codemods-telemetry-helpers'; +import { GlobSync } from 'glob'; +import path from 'node:path'; import { z } from 'zod'; import logger from './log-helper'; +import { type UserOptions } from './options'; const RuntimeDataSchema = z.object({ type: z.string().optional(), @@ -18,8 +23,22 @@ export type RuntimeData = z.infer; * Gets telemetry data for the file and parses it into a valid `RuntimeData` * object. */ -export function getRuntimeData(filePath: string): RuntimeData { +export function getRuntimeData( + filePath: string, + { moduleRoot, packageBase }: UserOptions +): RuntimeData { let rawTelemetry = getTelemetryFor(path.resolve(filePath)); + + // FIXME: use either moduleRoot or packageBase?? + if (!rawTelemetry && moduleRoot && packageBase) { + const modulePath = getModulePathFor(path.resolve(filePath), { + moduleRoot, + packageBase, + }); + const moduleKey = generateModuleKey(modulePath); + rawTelemetry = getTelemetry()[moduleKey]; + } + if (!rawTelemetry) { // Do not re-throw. The most likely reason this happened was because // the user's app threw an error. We still want the codemod to work if so. @@ -50,3 +69,45 @@ class RuntimeDataError extends Error { this.name = 'RuntimeDataError'; } } + +/** + * Transforms a literal "on disk" path to a "module path". + * + * @param filePath the path on disk (from current working directory) + * @returns The in-browser module path for the specified filePath + */ +function getModulePathFor( + filePath: string, + { moduleRoot, packageBase }: { moduleRoot: string; packageBase: string } +): string { + const rootPaths = new GlobSync(`**/${packageBase}`, { + ignore: ['**/tmp/**', '**/node_modules/**'], + absolute: true, + }).found; + + let bestMatch = ''; + let relativePath; + + for (const rootPath of rootPaths) { + if (filePath.startsWith(rootPath) && rootPath.length > bestMatch.length) { + bestMatch = rootPath; + relativePath = filePath.slice( + rootPath.length + 1 /* for slash */, + -path.extname(filePath).length + ); + } + } + + if (relativePath) { + return `${moduleRoot}/${relativePath}`; + } + + // FIXME: Better error + throw new Error('Could not determine module path'); +} + +function generateModuleKey(modulePath: string): string { + const moduleKey = modulePath.replace('templates/components/', 'components/'); + // If `templates/` still exists in the path then it wasn't a component but a controller-level template instead + return moduleKey.replace('templates/', 'controllers/'); +} diff --git a/transforms/helpers/transform.ts b/transforms/helpers/transform.ts index 1f38831..db95b82 100644 --- a/transforms/helpers/transform.ts +++ b/transforms/helpers/transform.ts @@ -77,7 +77,7 @@ function _maybeTransformEmberObjects( } else { const options: Options = { ...userOptions, - runtimeData: getRuntimeData(filePath), + runtimeData: getRuntimeData(filePath, userOptions), }; // eslint-disable-next-line unicorn/no-array-for-each diff --git a/types/ember-codemods-telemetry-helpers.d.ts b/types/ember-codemods-telemetry-helpers.d.ts index 03b835f..0556b2a 100644 --- a/types/ember-codemods-telemetry-helpers.d.ts +++ b/types/ember-codemods-telemetry-helpers.d.ts @@ -1,4 +1,7 @@ declare module 'ember-codemods-telemetry-helpers' { export function getTelemetryFor(filePath: string): unknown; + export function getTelemetry( + cacheKey?: string | undefined + ): Record; export function setTelemetry(telemetry: unknown): void; }