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

feat(cli-lib): adding support for gts, gjs and hbs files. #4376

Closed
wants to merge 15 commits into from
Closed
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
2 changes: 1 addition & 1 deletion .swcrc
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@
},
"keepClassNames": false
}
}
}
17 changes: 16 additions & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
const config = {
jsc: {
parser: {
syntax: 'typescript',
tsx: true,
dynamicImport: true,
topLevelAwait: true,
importMeta: true,
},
target: 'es2022',
},
}
module.exports = {
testEnvironment: 'node',
transform: {
'^.+\\.(t|j)sx?$': '@swc/jest',
'^.+\\.m?(t|j)sx?$': ['@swc/jest', config],
'^.+\\.m(t|j)s$': ['@swc/jest', config],
},
verbose: true,
moduleFileExtensions: ['js', 'jsx', 'mjs', 'ts', 'tsx', 'mts'],
extensionsToTreatAsEsm: ['.mts'],
}
24 changes: 18 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,19 @@
"@bazel/ibazel": "0.24",
"@commitlint/cli": "17",
"@commitlint/config-angular": "17",
"@glimmer/env": "^0.1.7",
"@glimmer/reference": "^0.91.1",
"@glimmer/syntax": "^0.91.1",
"@glimmer/validator": "^0.91.1",
"@jest/transform": "29",
"@jest/types": "29",
"@lerna-lite/cli": "2",
"@lerna-lite/list": "2",
"@lerna-lite/version": "2",
"@napi-rs/cli": "^2.11.0",
"@swc-node/register": "^1.5.4",
"@swc/core": "^1.3.56",
"@swc/jest": "^0.2.23",
"@swc-node/register": "^1.9.0",
"@swc/core": "^1.4.12",
"@swc/jest": "^0.2.36",
"@taplo/cli": "^0.5.0",
"@testing-library/jest-dom": "^6.0.0",
"@testing-library/react": "^14.0.0",
Expand Down Expand Up @@ -88,6 +92,7 @@
"clsx": "2",
"commander": "8",
"core-js": "^3.6.5",
"ember-template-recast": "^6.1.4",
"emoji-regex": "^10.2.1",
"eslint": "7 || 8",
"fast-glob": "^3.2.7",
Expand All @@ -96,7 +101,7 @@
"http-server": "^14.0.0",
"husky": "^8.0.0",
"jasmine-expect": "^5.0.0",
"jest": "29",
"jest": "^29.7.0",
"jest-environment-jsdom": "29",
"json-stable-stringify": "^1.0.1",
"karma": "^6.0.4",
Expand All @@ -118,8 +123,8 @@
"rimraf": "^3.0.2",
"serialize-javascript": "^6.0.0",
"test262-harness": "10",
"ts-jest": "29",
"ts-loader": "^9.1.2",
"ts-jest": "^29.1.2",
"ts-loader": "^9.5.1",
"tsd": "^0.30.0",
"tslib": "^2.4.0",
"typescript": "5.2.2",
Expand All @@ -140,6 +145,12 @@
"source-map-js": "1.0.2"
},
"pnpm": {
"overrides": {
"ember-template-recast": "^6.1.4",
"@glimmer/reference": "^0.91.1",
"@glimmer/syntax": "^0.91.1",
"@glimmer/validator": "^0.91.1"
},
"packageExtensions": {
"ts-jest": {
"dependencies": {
Expand Down Expand Up @@ -172,6 +183,7 @@
"author": "Seth Bertalotto <sbertal@verizonmedia.com>",
"dependencies": {
"@swc/helpers": "^0.5.1",
"content-tag": "^2.0.1",
"jest-cli": "^29.5.0",
"jest-junit": "^16.0.0",
"magic-string": "^0.30.0"
Expand Down
14 changes: 12 additions & 2 deletions packages/cli-lib/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ npm_package(
visibility = ["//visibility:public"],
)

SRCS = glob(["src/**/*.ts"]) + [
SRCS = glob(["src/**/*.ts", "src/**/*.mts"]) + [
"index.ts",
"main.ts",
# "package.json",
Expand All @@ -35,6 +35,16 @@ VUE_DEPS = [
"//:node_modules/vue",
]

GLIMMER_HBS_DEPS = [
"//:node_modules/@glimmer/env",
"//:node_modules/@glimmer/reference",
"//:node_modules/@glimmer/syntax",
"//:node_modules/@glimmer/validator",
"//:node_modules/content-tag",
"//:node_modules/ember-template-recast",
":node_modules/@babel/parser",
]

SRC_DEPS = [
"//:node_modules/@types/estree",
"//:node_modules/@types/fs-extra",
Expand All @@ -49,7 +59,7 @@ SRC_DEPS = [
"//:node_modules/typescript",
":node_modules/@formatjs/icu-messageformat-parser",
":node_modules/@formatjs/ts-transformer",
] + VUE_DEPS
] + VUE_DEPS + GLIMMER_HBS_DEPS

ts_compile_node(
name = "dist",
Expand Down
7 changes: 7 additions & 0 deletions packages/cli-lib/main.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env node

'use strict'

import cli from './src/cli.js'

cli.default(process.argv)
5 changes: 0 additions & 5 deletions packages/cli-lib/main.ts

This file was deleted.

11 changes: 11 additions & 0 deletions packages/cli-lib/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,20 @@
"url": "https://github.com/formatjs/formatjs/issues"
},
"dependencies": {
"@babel/parser": "^7.22.10",
"@formatjs/icu-messageformat-parser": "workspace:*",
"@formatjs/ts-transformer": "workspace:*",
"@glimmer/env": "^0.1.7",
"@glimmer/reference": "^0.91.1",
"@glimmer/syntax": "^0.91.1",
"@glimmer/validator": "^0.91.1",
"@types/estree": "^1.0.0",
"@types/fs-extra": "^9.0.1",
"@types/json-stable-stringify": "^1.0.32",
"@types/node": "14 || 16 || 17",
"chalk": "^4.0.0",
"commander": "8",
"ember-template-recast": "^6.1.4",
"fast-glob": "^3.2.7",
"fs-extra": "10",
"json-stable-stringify": "^1.0.1",
Expand All @@ -60,5 +66,10 @@
},
"engines": {
"node": ">= 16"
},
"devDependencies": {
"vite": "^5.2.8",
"vite-plugin-dts": "^3.8.1",
"vitest": "^1.4.0"
}
}
16 changes: 15 additions & 1 deletion packages/cli-lib/src/extract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,20 @@ async function processFile(
debug('Processing %s using vue extractor', fn)
const {parseFile} = await import('./vue_extractor.js')
parseFile(source, fn, scriptParseFn)
} else if (fn.endsWith('.hbs')) {
debug('Processing %s using hbs extractor', fn)
// SAFETY: The TS config does not understand that this is a
// safe way to import a module in a cjs project
// @ts-ignore
const {parseFile} = await import('./hbs_extractor.mjs')
parseFile(source, fn, opts)
} else if (fn.endsWith('.gts') || fn.endsWith('.gjs')) {
debug('Processing %s as gts/gjs file', fn)
// SAFETY: The TS config does not understand that this is a
// safe way to import a module in a cjs project
// @ts-ignore
const {parseFile} = await import('./gts_extractor.mjs')
parseFile(source, fn, opts)
} else {
debug('Processing %s using typescript extractor', fn)
scriptParseFn(source)
Expand Down Expand Up @@ -219,7 +233,7 @@ export async function extract(
const {id, description, defaultMessage} = message
if (!id) {
const error = new Error(
`[FormatJS CLI] Missing message id for message:
`[FormatJS CLI] Missing message id for message:
${JSON.stringify(message, undefined, 2)}`
)
if (throws) {
Expand Down
18 changes: 18 additions & 0 deletions packages/cli-lib/src/gts_extractor.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {parseFile as parseHbsFile} from './hbs_extractor.mjs'
import {parseScript} from './parse_script.js'
import {Preprocessor} from 'content-tag'
let p = new Preprocessor()

export function parseFile(source: string, fileName: string, options: any) {
const scriptParseFn = parseScript(options, fileName)
const transformedSource = p.process(source, {filename: fileName})

scriptParseFn(transformedSource)

// extract template from transformed source to then run through hbs processor
const parseResult = p.parse(source, {filename: fileName})

for (let parsed of parseResult) {
parseHbsFile(parsed.contents, fileName, options)
}
}
59 changes: 59 additions & 0 deletions packages/cli-lib/src/hbs_extractor.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import {transform} from 'ember-template-recast'
import type {AST} from '@glimmer/syntax'
import {Opts} from '@formatjs/ts-transformer'

function extractText(
node: AST.MustacheStatement | AST.SubExpression,
fileName: string,
options: Opts
) {
if (!options.onMsgExtracted) return
if (!options.overrideIdFn) return

if (node.path.type !== 'PathExpression') return

if (['format-message', 'formatMessage'].includes(node.path.original)) {
let [first, second] = node.params

if (first.type !== 'StringLiteral') return

let message = first?.value

let desc: string | undefined
if (second?.type === 'StringLiteral') {
desc = second.value?.trim().replace(/\s+/gm, ' ')
}

let defaultMessage = message?.trim().replace(/\s+/gm, ' ')

let id =
typeof options.overrideIdFn === 'string'
? options.overrideIdFn
: options.overrideIdFn(undefined, defaultMessage, desc, fileName)

options.onMsgExtracted(fileName, [
{
id: id,
defaultMessage: defaultMessage,
description: desc,
},
])
}
}

export function parseFile(source: string, fileName: string, options: any) {
let visitor = function () {
return {
MustacheStatement(node: AST.MustacheStatement) {
extractText(node, fileName, options)
},
SubExpression(node: AST.SubExpression) {
extractText(node, fileName, options)
},
}
}

// SAFETY: ember-template-recast's types are out of date,
// but it does not affect runtime
transform(source, visitor as any)
}