Skip to content

Commit

Permalink
Merge pull request #61 from bywhitebird/hotfix/kaz-ast-import-path-fix
Browse files Browse the repository at this point in the history
Hotfix/kaz ast import path fix
  • Loading branch information
arthur-fontaine authored Oct 27, 2023
2 parents 5f26ef7 + 4dde12b commit 04478cc
Show file tree
Hide file tree
Showing 17 changed files with 153 additions and 77 deletions.
66 changes: 48 additions & 18 deletions apps/kazam/src/application/usecases/generate.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type * as fs from 'node:fs'
import * as path from 'node:path'

import type { TransformerOutput } from '@whitebird/kazam-transformer-base'
import kebabCase from 'just-kebab-case'
Expand All @@ -21,30 +22,49 @@ const writeResults = (
})
}

const getTransformerDirectory = (transformer: Transformer, config: Exclude<KazamConfig, unknown[]>) => {
const transformerDirectory = [
config.output,
kebabCase(transformer.name.replace(/^Transformer/, '')),
].join('/')

return transformerDirectory
}

const getTransformedOutputFilePath = (
filePath: string,
sourceFilePath: string,
transformer: Transformer,
config: Exclude<KazamConfig, unknown[]>,
configPath: string,
) => {
const sourceExtension = `.${sourceFilePath.split('.').slice(-1)[0]}` ?? ''
const transformedExtension = `.${filePath.split('.').slice(-1)[0]}` ?? ''

const transformerDirectory = getTransformerDirectory(transformer, config)

const outputFilePath = path.resolve(
path.dirname(configPath),
transformerDirectory,
filePath.replace(
new RegExp(`${sourceExtension.replace('.', '\\.')}${transformedExtension.replace('.', '\\.')}$`),
transformedExtension,
),
)

return outputFilePath
}

const formatResults = (
transformerResult: TransformerOutput<{ outputFileNameFormat: string }>,
transformer: Transformer,
config: Exclude<KazamConfig, unknown[]>,
configPath: string,
): Parameters<typeof writeResults>[0] => {
const formattedTransformerResult = new Map<string, string>()

const transformerDirectory = [
config.output,
kebabCase(transformer.name.replace(/^Transformer/, '')),
].join('/')

transformerResult.forEach(({ filePath, content }, sourceFilePath) => {
const sourceExtension = `.${sourceFilePath.split('.').slice(-1)[0]}` ?? ''
const transformedExtension = `.${filePath.split('.').slice(-1)[0]}` ?? ''

const outputFilePath = [
transformerDirectory,
// The following line replaces `.kaz.tsx` with `.tsx` (for example)
filePath.replace(
new RegExp(`${sourceExtension.replace('.', '\\.')}${transformedExtension.replace('.', '\\.')}$`),
transformedExtension,
),
].join('/')
const outputFilePath = getTransformedOutputFilePath(filePath, sourceFilePath, transformer, config, configPath)

formattedTransformerResult.set(outputFilePath, content)
})
Expand All @@ -61,19 +81,29 @@ const generateForConfig = async (
config.parsers.map(async (ParserClass) => {
const parser = new ParserClass()

const transformerInput = await parser.loadAndParse({
const parserOutput = await parser.loadAndParse({
...config,
configPath,
})

return config.transformers.map((TransformerClass) => {
const transformerInput = Object.fromEntries(
Object.entries(parserOutput).map(([key, value]) => {
return [key, {
...value,
getTransformedOutputFilePath: (filePath: string) =>
getTransformedOutputFilePath(filePath, value.sourceAbsoluteFilePath, TransformerClass, config, configPath),
}] as const
}),
)

const transformer = new TransformerClass(transformerInput, {})

const transformerResult = transformer.transform()

if (fileSystem) {
writeResults(
formatResults(transformerResult, TransformerClass, config),
formatResults(transformerResult, TransformerClass, config, configPath),
fileSystem,
)
}
Expand Down
4 changes: 2 additions & 2 deletions packages/parser-base/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ interface ParserConfig {

export abstract class ParserBase<LoadResult> {
abstract load(config: ParserConfig): Promise<LoadResult>
abstract parse(loadResult: LoadResult, config: ParserConfig): Promise<TransformerInput>
abstract parse(loadResult: LoadResult, config: ParserConfig): Promise<Record<string, Omit<TransformerInput[string], 'getTransformedOutputFilePath'>>>

async loadAndParse(config: ParserConfig) {
async loadAndParse(config: ParserConfig): ReturnType<ParserBase<LoadResult>['parse']> {
return this.load(config).then(loadResult => this.parse(loadResult, config))
}
}
17 changes: 7 additions & 10 deletions packages/parser-kaz/src/parser-kaz.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,8 @@ import * as path from 'node:path'

import { parse, tokenize } from '@whitebird/kaz-ast'
import { ParserBase } from '@whitebird/kazam-parser-base'
import type { TransformerInput } from '@whitebird/kazam-transformer-base'
import { glob } from 'glob'

import { fixAstImportPaths } from './utils/fix-ast'

export class ParserKaz extends ParserBase<{
pathRelativeToInputPath: string
inputPath: string
Expand Down Expand Up @@ -41,9 +38,11 @@ export class ParserKaz extends ParserBase<{
pathRelativeToInputPath: string
inputPath: string
}[],
{ input, output }: Parameters<ParserBase<string[]>['parse']>[1],
) {
const kazAsts: TransformerInput = {}
const kazAsts: Awaited<ReturnType<ParserBase<{
pathRelativeToInputPath: string
inputPath: string
}[]>['parse']>> = {}

for (const { inputPath, pathRelativeToInputPath } of kazFiles) {
const filePath = path.join(inputPath, pathRelativeToInputPath)
Expand All @@ -59,12 +58,10 @@ export class ParserKaz extends ParserBase<{
if (ast === undefined)
throw new Error(`Could not parse file ${filePath}`)

const fixedAst = fixAstImportPaths(
kazAsts[pathRelativeToInputPath] = {
ast,
{ filePath, input, output },
)

kazAsts[pathRelativeToInputPath] = fixedAst
sourceAbsoluteFilePath: filePath,
}
}

return kazAsts
Expand Down
22 changes: 0 additions & 22 deletions packages/parser-kaz/src/utils/fix-ast.ts

This file was deleted.

6 changes: 4 additions & 2 deletions packages/parser-kaz/tests/parser-kaz.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ describe('parser-kaz', () => {
const parser = new ParserKaz()

describe('loadAndParse', () => {
test('should return an object of ASTs with keys relative to the input path', async () => {
test('should return an object of objects with AST and absolute input path with keys relative to the input path', async () => {
const result = await parser.loadAndParse(config)

Object.values(result).forEach((ast) => {
Object.values(result).forEach(({ ast, sourceAbsoluteFilePath }) => {
expect(ast.$type).toBe('Kaz')
expect(sourceAbsoluteFilePath).toSatisfy(path.isAbsolute)
expect(sourceAbsoluteFilePath).toMatch(/^\/.+fixtures\/(Input|buttons\/PrimaryButton|buttons\/SecondaryButton)\.kaz$/)
})

expect(result).toMatchObject({
Expand Down
3 changes: 2 additions & 1 deletion packages/storybook-plugin-kaz-react-vite/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"kazam": "workspace:*"
},
"devDependencies": {
"@types/node": "^18.11.9"
"@types/node": "^18.11.9",
"vite": "^4.4.9"
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { parse, tokenize } from '@whitebird/kaz-ast'
import type { ITransformerInput } from '@whitebird/kazam-transformer-base'
import type { TransformerInput } from '@whitebird/kazam-transformer-base'
import type { Page } from 'playwright'

export interface TestWebTransformerFixture {
fixtureDirectory: string
input: ITransformerInput & { 'Index': ITransformerInput['Index'] }
input: TransformerInput & { 'Index': TransformerInput['Index'] }
scenario: (page: Page) => Promise<void>
}

Expand All @@ -25,7 +25,11 @@ export const createTestWebTransformerFixture = async (fixture: TestWebTransforme
if (ast instanceof Error || ast === undefined)
throw new Error(`Failed to parse ${key} in ${fixture.fixtureDirectory}`)

return [key, ast] as const
return [key, {
ast,
sourceAbsoluteFilePath: '',
getTransformedOutputFilePath: (filePath: string) => filePath,
}] as const
}),
) as TestWebTransformerFixture['input'],
}
Expand Down
7 changes: 6 additions & 1 deletion packages/transformer-base/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ export type TransformerInput<
_Options extends TransformerSettings = TransformerSettings,
> = Record<
string,
zod.infer<typeof kazAstSchema>
{
ast: zod.infer<typeof kazAstSchema>
sourceAbsoluteFilePath: string
// eslint-disable-next-line @typescript-eslint/no-unused-vars, unused-imports/no-unused-vars
getTransformedOutputFilePath: (filePath: string) => string
}
>

export type TransformerOutput<
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import * as path from 'node:path'

import { Effect, Ref } from 'effect'

import { ImportStateService } from '../states/import-state'
Expand All @@ -18,6 +20,21 @@ export const getImportString = () =>
return ''

return imports.map((import_) => {
// The following block will fix the import path if it is a relative path
if (import_.path.startsWith('.')) {
const absoluteImportedFilePath = path.resolve(
path.dirname(metadata.sourceAbsoluteFilePath),
import_.path,
)

const relativeImportedFilePath = path.relative(
path.dirname(metadata.outputAbsoluteFilePath),
absoluteImportedFilePath,
)

import_.path = `./${relativeImportedFilePath}`
}

if (import_.type === 'sideEffectImport')
return `import '${import_.path}'`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ const findAssignmentsToStateVariable = (

const metadata = yield * _(transformService.getMetadata())

const kazAst = metadata.input[metadata.filePath]
const kazAst = metadata.input[metadata.filePath]?.ast

if (kazAst === undefined)
throw new Error(`No AST found for file ${metadata.filePath}`)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import type { TransformerReact } from '../../../transformer-react'
export interface MetadataState extends Ref.Ref<{
componentName: string
filePath: string
sourceAbsoluteFilePath: string
outputAbsoluteFilePath: string
input: TransformerReact['input']
}> { }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ export const transform = ({ input }: Pick<TransformerReact, 'input' | 'options'>
yield * _(transformService.setMetadata({
componentName: yield * _(transformService.getComponentName(filePath)),
filePath,
sourceAbsoluteFilePath: file.sourceAbsoluteFilePath,
outputAbsoluteFilePath: file.getTransformedOutputFilePath(`${filePath}.tsx`),
input,
}))

const transformed = yield * _(
pipe(
transformService.handle(file),
transformService.handle(file.ast),
Effect.map(transformed =>
prettier.format(transformed, {
parser: 'babel-ts',
Expand Down
18 changes: 11 additions & 7 deletions packages/transformer-react/tests/factories/transform-input.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import { parse, tokenize } from '@whitebird/kaz-ast'
import type { ITransformerInput } from '@whitebird/kazam-transformer-base'
import type { TransformerInput } from '@whitebird/kazam-transformer-base'
import deepmerge from 'deepmerge'

const defaultInput: ITransformerInput[string] = {
$type: 'Kaz',
instructions: [],
template: [],
const defaultInput: TransformerInput[string] = {
ast: {
$type: 'Kaz',
instructions: [],
template: [],
},
sourceAbsoluteFilePath: '',
getTransformedOutputFilePath: (filePath: string) => filePath,
}

export class TransformerInputFactory {
static async create(input: Partial<ITransformerInput[number]> | string = {}) {
static async create(input: Partial<TransformerInput[number]> | string = {}) {
if (typeof input === 'string') {
const tokens = tokenize(input)
const ast = parse(tokens)
Expand All @@ -23,7 +27,7 @@ export class TransformerInputFactory {
return { Test: deepmerge(defaultInput, input) }
}

static createMany(...inputs: Partial<ITransformerInput[number]>[]) {
static createMany(...inputs: Partial<TransformerInput[number]>[]) {
return inputs.flatMap(input => TransformerInputFactory.create(input))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,10 @@ export class TransformerTypescript extends TransformerBase<{ outputFileNameForma
for (const componentName in this.input) {
const component = this.input[componentName]

this.handle(component, { name: componentName })
if (component === undefined)
continue

this.handle(component.ast, { name: componentName })
}

return Object.entries(this.generatedContent).reduce<{
Expand Down
11 changes: 9 additions & 2 deletions packages/transformer-vue/src/transformer-vue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,19 @@ export class TransformerVue extends TransformerBase<{
for (const componentName in this.input) {
const component = this.input[componentName]

const result = this.handle(component, { name: componentName })
if (component === undefined)
continue

const result = this.handle(component.ast, { name: componentName })

this.generatedComponents[componentName] = `
<script lang="ts">\n${prettier.format(
`
${importsToString(mergeImports(this.imports[componentName] ?? []))}
${importsToString(
mergeImports(this.imports[componentName] ?? []),
component.sourceAbsoluteFilePath,
component.getTransformedOutputFilePath(`${componentName}.vue`),
)}
${result}
`,
Expand Down
Loading

0 comments on commit 04478cc

Please sign in to comment.