1- import { getTelemetryFor } from 'ember-codemods-telemetry-helpers' ;
2- import path from 'path' ;
1+ import {
2+ getTelemetry ,
3+ getTelemetryFor ,
4+ } from 'ember-codemods-telemetry-helpers' ;
5+ import { GlobSync } from 'glob' ;
6+ import path from 'node:path' ;
37import { z } from 'zod' ;
48import logger from './log-helper' ;
9+ import { type UserOptions } from './options' ;
510
611const RuntimeDataSchema = z . object ( {
712 type : z . string ( ) . optional ( ) ,
@@ -18,8 +23,22 @@ export type RuntimeData = z.infer<typeof RuntimeDataSchema>;
1823 * Gets telemetry data for the file and parses it into a valid `RuntimeData`
1924 * object.
2025 */
21- export function getRuntimeData ( filePath : string ) : RuntimeData {
26+ export function getRuntimeData (
27+ filePath : string ,
28+ { moduleRoot, packageBase } : UserOptions
29+ ) : RuntimeData {
2230 let rawTelemetry = getTelemetryFor ( path . resolve ( filePath ) ) ;
31+
32+ // FIXME: use either moduleRoot or packageBase??
33+ if ( ! rawTelemetry && moduleRoot && packageBase ) {
34+ const modulePath = getModulePathFor ( path . resolve ( filePath ) , {
35+ moduleRoot,
36+ packageBase,
37+ } ) ;
38+ const moduleKey = generateModuleKey ( modulePath ) ;
39+ rawTelemetry = getTelemetry ( ) [ moduleKey ] ;
40+ }
41+
2342 if ( ! rawTelemetry ) {
2443 // Do not re-throw. The most likely reason this happened was because
2544 // the user's app threw an error. We still want the codemod to work if so.
@@ -50,3 +69,45 @@ class RuntimeDataError extends Error {
5069 this . name = 'RuntimeDataError' ;
5170 }
5271}
72+
73+ /**
74+ * Transforms a literal "on disk" path to a "module path".
75+ *
76+ * @param filePath the path on disk (from current working directory)
77+ * @returns The in-browser module path for the specified filePath
78+ */
79+ function getModulePathFor (
80+ filePath : string ,
81+ { moduleRoot, packageBase } : { moduleRoot : string ; packageBase : string }
82+ ) : string {
83+ const rootPaths = new GlobSync ( `**/${ packageBase } ` , {
84+ ignore : [ '**/tmp/**' , '**/node_modules/**' ] ,
85+ absolute : true ,
86+ } ) . found ;
87+
88+ let bestMatch = '' ;
89+ let relativePath ;
90+
91+ for ( const rootPath of rootPaths ) {
92+ if ( filePath . startsWith ( rootPath ) && rootPath . length > bestMatch . length ) {
93+ bestMatch = rootPath ;
94+ relativePath = filePath . slice (
95+ rootPath . length + 1 /* for slash */ ,
96+ - path . extname ( filePath ) . length
97+ ) ;
98+ }
99+ }
100+
101+ if ( relativePath ) {
102+ return `${ moduleRoot } /${ relativePath } ` ;
103+ }
104+
105+ // FIXME: Better error
106+ throw new Error ( 'Could not determine module path' ) ;
107+ }
108+
109+ function generateModuleKey ( modulePath : string ) : string {
110+ const moduleKey = modulePath . replace ( 'templates/components/' , 'components/' ) ;
111+ // If `templates/` still exists in the path then it wasn't a component but a controller-level template instead
112+ return moduleKey . replace ( 'templates/' , 'controllers/' ) ;
113+ }
0 commit comments