Skip to content

Commit

Permalink
added new helper to generate glob file imports
Browse files Browse the repository at this point in the history
  • Loading branch information
MP281X committed May 12, 2024
1 parent f0cf22e commit 0314f48
Show file tree
Hide file tree
Showing 16 changed files with 323 additions and 138 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# generated/cache files
**/dist
**/*.g.ts
**/.eslintcache

# other files/folders
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
```jsonc
{
"extends": ["@mp281x/shared-config/tsconfig"],
"include": ["index.ts", "src/**/*", "*.config.*", "vitest.*"],
"include": ["index.ts", "src/**/*", "*.config.*", "vitest.*", "imports.g.ts"],
"exclude": ["**/node_modules", "**/.*/", "**/dist", "**/build"]
}
```
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"scripts": {
"dev": "tsup-node --watch",
"fix": "./dist/task-runner.js --fix",
"test": "./dist/task-runner.js --test",
"check": "./dist/task-runner.js --check --test"
},

Expand Down
165 changes: 92 additions & 73 deletions src/eslint.ts

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/lib/cliHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const getArgs = () => {
if (flags.includes('--dev')) flags.push('--run')
if (task === 'dev') flags.push('--dev')

return { flags, task, cmd: args }
return { cmd: args, flags, task }
}

export const handleKeypress = () => {
Expand Down
8 changes: 4 additions & 4 deletions src/lib/execCmd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const getPackageManager = () => {
export const asyncCommands: Promise<void>[] = []

type ExecCmd = { cwd?: string; title: string; cmd: string[]; customCmd?: string; mode: 'sync' | 'async' }
export const execCmd = async ({ title, cmd, customCmd, mode, cwd }: ExecCmd) => {
export const execCmd = async ({ cmd, customCmd, cwd, mode, title }: ExecCmd) => {
const execPromise = new Promise<void>((resolve, _) => {
const output = spawn(customCmd ?? getPackageManager(), cmd, {
cwd: cwd ?? process.cwd(),
Expand All @@ -42,16 +42,16 @@ export const execCmd = async ({ title, cmd, customCmd, mode, cwd }: ExecCmd) =>
}

type ReadLogFile = { cwd: string; title: string }
export const readLogFile = async ({ title, cwd }: ReadLogFile) => {
export const readLogFile = async ({ cwd, title }: ReadLogFile) => {
// clear the log file
const logFile = `${cwd}/lsp-plugin.log`
if (fs.existsSync(logFile)) fs.rmSync(logFile)
fs.writeFileSync(logFile, '')

await execCmd({
title: `${title}:lsp-logs`,
cmd: ['-f', logFile],
customCmd: 'tail',
mode: 'async'
mode: 'async',
title: `${title}:lsp-logs`
})
}
15 changes: 9 additions & 6 deletions src/lib/findGlob.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,17 @@ import { parseConfig } from './parseConfig'

const gitIgnore = parseConfig<string[]>(`${process.cwd()}/.gitignore`) ?? []

export const findGlob: typeof fs.globSync = (glob, options) =>
fs.globSync(glob, {
cwd: options?.cwd ?? process.cwd(),
exclude: path => options?.exclude?.(path) || gitIgnore.includes(path)
})
export const findGlob: typeof fs.globSync = (glob, options) => {
if (options === undefined) options = {}
if (options.cwd === undefined) options.cwd = process.cwd()

return fs
.globSync(glob, { cwd: options.cwd, exclude: path => options.exclude?.(path) || gitIgnore.includes(path) })
.filter(file => fs.statSync(`${options.cwd}/${file}`).isFile())
}

if (import.meta.vitest) {
const { it, assert } = import.meta.vitest
const { assert, it } = import.meta.vitest

it('glob search', () => {
const results = findGlob('*.ts', { cwd: __dirname })
Expand Down
24 changes: 15 additions & 9 deletions src/lib/findProjects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,14 @@ const getWorkspaceProjects = (dir: string) => {
return globs.flatMap(glob => findGlob(glob, { cwd: dir })).filter(path => fs.existsSync(`${dir}/${path}/package.json`))
}

export type Project = { cwd: string; name: string; scripts: string[]; lspPlugin: boolean; type: 'node' | 'svelte' }
export type Project = {
cwd: string
name: string
scripts: string[]
lspPlugins: string[]
globImports: string[]
type: 'node' | 'svelte'
}
// find all the projects in a the monorepo/repo
export const findProjects = (dir: string = process.cwd()): Project[] => {
const projects = getWorkspaceProjects(dir).flatMap(project => findProjects(project))
Expand All @@ -25,22 +32,21 @@ export const findProjects = (dir: string = process.cwd()): Project[] => {
if (!fs.existsSync(`${dir}/package.json`)) return []

// load the package info
type PackageJSON = { scripts?: Record<string, string>; devDependencies?: Record<string, string> }
const { scripts, devDependencies } = parseConfig<PackageJSON>(`${dir}/package.json`) ?? {}
type PackageJSON = { globImports?: string[]; scripts?: Record<string, string>; devDependencies?: Record<string, string> }
const { devDependencies, globImports, scripts } = parseConfig<PackageJSON>(`${dir}/package.json`) ?? {}

// read the tsconfig and check if the project is using the custom lsp plugin
type TSConfig = { compilerOptions?: { plugins?: { name: string }[] } }
const { compilerOptions } = parseConfig<TSConfig>(`${dir}/tsconfig.json`) ?? {}

const lspPlugin = compilerOptions?.plugins?.find(x => x.name === 'lsp-plugin') !== undefined

return [
{
scripts: Object.keys(scripts ?? {}),
cwd: dir,
globImports: globImports ?? [],
lspPlugins: compilerOptions?.plugins?.map(({ name }) => name) ?? [],
name: dir.split('/').pop() ?? '',
type: devDependencies?.['svelte'] ? 'svelte' : 'node',
lspPlugin,
cwd: dir
scripts: Object.keys(scripts ?? {}),
type: devDependencies?.['svelte'] ? 'svelte' : 'node'
}
]
}
59 changes: 59 additions & 0 deletions src/lib/globImports.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import type { Expression } from 'typescript'

import fs from 'fs'

import { findGlob } from './findGlob'
import { nodeToStr, constFactory, objectFactory, asyncArrowFnFactory, defaultExportFactory, dynamicImportFactory } from './tsFactory.ts'

const fileImportsObj = (glob: string) => {
const fileImports = new Map<string, Expression>()

for (const file of findGlob(glob)) fileImports.set(file, dynamicImportFactory(`./${file}`))

return objectFactory(Object.fromEntries(fileImports))
}

const globImportsObj = (globImports: string[]) => {
const _globImports = new Map<string, Expression>()

for (const glob of globImports) _globImports.set(glob, asyncArrowFnFactory(fileImportsObj(glob)))

return objectFactory(Object.fromEntries(_globImports))
}

type GenGlobImportsFile = { cwd: string; globImports: string[] }
export const genGlobImportsFile = ({ cwd, globImports }: GenGlobImportsFile) => {
const out: string[] = []

out.push(nodeToStr(constFactory('imports', globImportsObj(globImports))))

out.push(`
type Imports = {
[Glob in keyof typeof imports]: () => Promise<{
[File in keyof Awaited<ReturnType<(typeof imports)[Glob]>>]: {
[Export in keyof Awaited<ReturnType<(typeof imports)[Glob]>>[File]]: Awaited<ReturnType<(typeof imports)[Glob]>>[File][Export]
}
}>
}
`)

out.push(nodeToStr(defaultExportFactory('imports', 'Imports')))

fs.writeFileSync(`${cwd}/imports.g.ts`, out.join('\n\n'))
}

if (import.meta.vitest) {
const { expect, it } = import.meta.vitest

it('file imports obj', () => {
const code = nodeToStr(fileImportsObj('**/globImports.ts'))
const expectedCode = '{ "src/lib/globImports.ts": await import("./src/lib/globImports.ts") }'
expect(code.replaceAll('\n', ' ').replace(/\s+/g, ' ')).toEqual(expectedCode)
})

it('glob imports obj', () => {
const code = nodeToStr(globImportsObj(['**/noFiles']))
const expectedCode = '{ "**/noFiles": async () => ({}) }'
expect(code.replaceAll('\n', ' ').replace(/\s+/g, ' ')).toEqual(expectedCode)
})
}
6 changes: 3 additions & 3 deletions src/lib/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ const logFn = (type: LogType) => async (title: string, input: Input) => {
}

export const log = {
info: logFn('info'),
error: logFn('error')
error: logFn('error'),
info: logFn('info')
}

// give a task the same color for the process duration
Expand Down Expand Up @@ -98,7 +98,7 @@ const defaultLogFormatter = (input: string, printLog: (txt: string) => void, pre
const vitestLogFormatter = (input: string, printLog: (txt: string) => void) => {
let hasLogged = false

for (const { name, errors } of parseVitestOutput(input)) {
for (const { errors, name } of parseVitestOutput(input)) {
for (let error of errors) {
hasLogged = true

Expand Down
86 changes: 86 additions & 0 deletions src/lib/tsFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import ts from 'typescript'

export const constFactory = (name: string, expression: ts.Expression) =>
ts.factory.createVariableStatement(
undefined,
ts.factory.createVariableDeclarationList(
[ts.factory.createVariableDeclaration(ts.factory.createIdentifier(name), undefined, undefined, expression)],
ts.NodeFlags.Const
)
)

export const dynamicImportFactory = (path: string) =>
ts.factory.createAwaitExpression(
ts.factory.createCallExpression(
// @ts-expect-error
ts.factory.createToken(ts.SyntaxKind.ImportKeyword),
undefined,
[ts.factory.createStringLiteral(path)]
)
)

export const objectFactory = (obj: Record<string, ts.Expression>) =>
ts.factory.createObjectLiteralExpression(
Object.entries(obj).map(([key, value]) => ts.factory.createPropertyAssignment(ts.factory.createStringLiteral(key), value)),
true
)

export const asyncArrowFnFactory = (expression: ts.Expression) =>
ts.factory.createArrowFunction(
[ts.factory.createToken(ts.SyntaxKind.AsyncKeyword)],
undefined,
[],
undefined,
ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
ts.factory.createParenthesizedExpression(expression)
)

export const defaultExportFactory = (varName: string, typeName?: string) =>
ts.factory.createExportAssignment(
undefined,
undefined,
typeName ?
ts.factory.createAsExpression(
ts.factory.createIdentifier(varName),
ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(typeName), undefined)
)
: ts.factory.createIdentifier(varName)
)

// factory.createTypeReferenceNode(
// factory.createIdentifier("Z"),
// undefined
// )

export const nodeToStr = (node: ts.Node) =>
// @ts-expect-error
ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }).printNode(ts.EmitHint.Unspecified, node, undefined)

if (import.meta.vitest) {
const { expect, it } = import.meta.vitest

it('const', () => {
const code = nodeToStr(constFactory('x', ts.factory.createStringLiteral('value')))
expect(code).toEqual('const x = "value";')
})

it('dynamic import', () => {
const code = nodeToStr(dynamicImportFactory('./file.ts'))
expect(code).toEqual('await import("./file.ts")')
})

it('object', () => {
const code = nodeToStr(objectFactory({ key: ts.factory.createStringLiteral('value') }))
expect(code.replaceAll(' ', '').replaceAll('\n', '')).toEqual('{"key":"value"}')
})

it('async arrow fn', () => {
const code = nodeToStr(asyncArrowFnFactory(ts.factory.createStringLiteral('value')))
expect(code).toEqual('async () => ("value")')
})

it('default export', () => {
const code = nodeToStr(defaultExportFactory('var1'))
expect(code).toEqual('export default var1;')
})
}
14 changes: 7 additions & 7 deletions src/prettier.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import type { Config } from 'prettier'

export default {
semi: false,
useTabs: true,
printWidth: 150,
singleQuote: true,
trailingComma: 'none',
arrowParens: 'avoid',
quoteProps: 'consistent',
experimentalTernaries: true,
overrides: [{ files: '*.json', options: { parser: 'jsonc' } }],
plugins: ['prettier-plugin-svelte', 'prettier-plugin-astro', 'prettier-plugin-tailwindcss']
plugins: ['prettier-plugin-svelte', 'prettier-plugin-astro', 'prettier-plugin-tailwindcss'],
printWidth: 150,
quoteProps: 'consistent',
semi: false,
singleQuote: true,
trailingComma: 'none',
useTabs: true
} satisfies Config
Loading

0 comments on commit 0314f48

Please sign in to comment.