-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #6 from IgnaceMaes/content-tag-utils
- Loading branch information
Showing
9 changed files
with
248 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"ember-codemod-template-tag": patch | ||
--- | ||
|
||
Add utils for template tag AST and move hbs import removal to separate step |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
diff --git a/dist/ast/javascript.js b/dist/ast/javascript.js | ||
index fb12fdabf61cdcb199a904972abc3401f3955fc8..499a6f42016953aba891c4f10ada3cc861306ae1 100644 | ||
--- a/dist/ast/javascript.js | ||
+++ b/dist/ast/javascript.js | ||
@@ -63,6 +63,7 @@ const tsOptions = { | ||
['pipelineOperator', { proposal: 'minimal' }], | ||
'throwExpressions', | ||
'typescript', | ||
+ 'jsx', | ||
], | ||
}; | ||
function getParseOptions(isTypeScript) { |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,10 @@ | ||
import { convertTests, createOptions } from './steps/index.js'; | ||
import { removeHbsImport } from './steps/remove-import.js'; | ||
import type { CodemodOptions } from './types/index.js'; | ||
|
||
export function runCodemod(codemodOptions: CodemodOptions): void { | ||
const options = createOptions(codemodOptions); | ||
|
||
convertTests(options); | ||
removeHbsImport(options); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import { readFileSync } from 'node:fs'; | ||
import { join } from 'node:path'; | ||
|
||
import { createFiles, findFiles } from '@codemod-utils/files'; | ||
|
||
import type { Options } from '../types/index.js'; | ||
import { AST as AST_TEMPLATE_TAG } from '../utils/ast/template-tag.js'; | ||
import { isTypeScriptFile } from '../utils/general.js'; | ||
|
||
function removeImport( | ||
file: string, | ||
config: { appName: string; isTypeScript: boolean }, | ||
): string { | ||
const traverse = AST_TEMPLATE_TAG.traverse(config.isTypeScript); | ||
|
||
const { ast, contentTags } = traverse(file, { | ||
visitImportDeclaration(path) { | ||
if (path.value.source.value === 'ember-cli-htmlbars') { | ||
path.replace(); | ||
} | ||
return false; | ||
}, | ||
}); | ||
|
||
return AST_TEMPLATE_TAG.print(ast, contentTags); | ||
} | ||
|
||
export function removeHbsImport(options: Options): void { | ||
const { appName, projectRoot } = options; | ||
|
||
const filePaths = findFiles('**/*-test.{gjs,gts}', { | ||
projectRoot, | ||
}); | ||
|
||
const fileMap = new Map( | ||
filePaths.map((filePath) => { | ||
let file = readFileSync(join(projectRoot, filePath), 'utf8'); | ||
|
||
file = removeImport(file, { | ||
appName, | ||
isTypeScript: isTypeScriptFile(filePath), | ||
}); | ||
|
||
return [filePath, file]; | ||
}), | ||
); | ||
|
||
createFiles(fileMap, options); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
import { AST as AST_JS } from '@codemod-utils/ast-javascript'; | ||
import { Preprocessor } from 'content-tag'; | ||
import { types } from 'recast'; | ||
|
||
type Range = { | ||
end: number; | ||
start: number; | ||
}; | ||
|
||
type ContentTag = { | ||
contentRange: Range; | ||
contents: string; | ||
endRange: Range; | ||
range: Range; // range = startRange + contentRange + endRange | ||
startRange: Range; | ||
tagName: string; | ||
type: string; | ||
}; | ||
|
||
type ContentTagPlaceholder = { | ||
contents: string; | ||
id: string; | ||
}; | ||
|
||
export function parse(file: string) { | ||
const preprocessor = new Preprocessor(); | ||
|
||
return preprocessor.parse(file) as unknown as ContentTag[]; | ||
} | ||
|
||
export function replaceContents( | ||
file: string, | ||
options: { | ||
contents: string; | ||
range: Range; | ||
}, | ||
): string { | ||
const { contents, range } = options; | ||
|
||
return [ | ||
file.substring(0, range.start), | ||
'<template>', | ||
contents, | ||
'</template>', | ||
file.substring(range.end), | ||
].join(''); | ||
} | ||
|
||
function _print( | ||
ast: types.ASTNode, | ||
contentTags: ContentTagPlaceholder[], | ||
): string { | ||
let output = AST_JS.print(ast); | ||
|
||
const placeholderContentTags = parse(output); | ||
if (placeholderContentTags.length !== contentTags.length) { | ||
throw new Error('The number of content tags does not match'); | ||
} | ||
placeholderContentTags.reverse().forEach((placeholderContentTag) => { | ||
const match = contentTags.find( | ||
(contentTag) => contentTag.id === placeholderContentTag.contents, | ||
); | ||
if (match === undefined) { | ||
throw new Error( | ||
`Expected content tag with id "${placeholderContentTag.contents}" to exist, but no match was found.`, | ||
); | ||
} | ||
output = replaceContents(output, { | ||
contents: match.contents, | ||
range: placeholderContentTag.range, | ||
}); | ||
}); | ||
|
||
return output; | ||
} | ||
|
||
interface TraverseTT { | ||
ast: types.ASTNode; | ||
contentTags: ContentTagPlaceholder[]; | ||
} | ||
|
||
function _traverse( | ||
isTypeScript?: boolean, | ||
): (file: string, visitMethods?: types.Visitor) => TraverseTT { | ||
const originalTraverse = AST_JS.traverse(isTypeScript); | ||
return function ( | ||
file: string, | ||
visitMethods?: types.Visitor<unknown> | undefined, | ||
): TraverseTT { | ||
const contentTags = parse(file); | ||
const contentTagPlaceholders: ContentTagPlaceholder[] = []; | ||
contentTags.reverse().forEach((contentTag, index) => { | ||
const placeholderId = `${index}`; | ||
file = replaceContents(file, { | ||
contents: placeholderId, | ||
range: contentTag.range, | ||
}); | ||
contentTagPlaceholders.push({ | ||
contents: contentTag.contents, | ||
id: placeholderId, | ||
}); | ||
}); | ||
|
||
return { | ||
ast: originalTraverse(file, visitMethods), | ||
contentTags: contentTagPlaceholders, | ||
}; | ||
}; | ||
} | ||
|
||
/** | ||
* Provides methods from `recast` to help you parse and transform | ||
* `*.{gjs,gts}` files. | ||
* | ||
* @example | ||
* | ||
* ```ts | ||
* function transformCode(file: string, isTypeScript: boolean): string { | ||
* const traverse = AST.traverse(isTypeScript); | ||
* | ||
* const { ast, contentTags } = traverse(file, { | ||
* // Use AST.builders to transform the tree | ||
* }); | ||
* | ||
* return AST.print(ast, contentTags); | ||
* } | ||
* ``` | ||
*/ | ||
export const AST = { | ||
builders: AST_JS.builders, | ||
print: _print, | ||
traverse: _traverse, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { assertFixture, loadFixture, test } from '@codemod-utils/tests'; | ||
|
||
import { removeHbsImport } from '../../../src/steps/remove-import.js'; | ||
import { | ||
codemodOptions, | ||
options, | ||
} from '../../helpers/shared-test-setups/convert-tests.js'; | ||
|
||
test('steps | remove-import > base case', function () { | ||
const inputProject = { | ||
'example-test.gjs': | ||
"import { hbs } from 'ember-cli-htmlbars';\n<template>Test</template>\n", | ||
}; | ||
|
||
const outputProject = { | ||
'example-test.gjs': '<template>Test</template>\n', | ||
}; | ||
|
||
loadFixture(inputProject, codemodOptions); | ||
|
||
removeHbsImport(options); | ||
|
||
assertFixture(outputProject, codemodOptions); | ||
}); |