Skip to content

Commit

Permalink
fix: Repair module loading logic and have cosmiconfig support ESM+TS
Browse files Browse the repository at this point in the history
  • Loading branch information
Sidnioulz committed Sep 12, 2023
1 parent 95935aa commit c45ac70
Show file tree
Hide file tree
Showing 9 changed files with 94 additions and 48 deletions.
2 changes: 1 addition & 1 deletion __mocks__/cosmiconfig.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module.exports = {
cosmiconfigSync: jest.fn(),
cosmiconfig: jest.fn(),
}
2 changes: 1 addition & 1 deletion index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export type { VueTransformation } from '~/types/VueTransformation'
export type { Config as SfcmodConfig } from '~/config.schema'

export { default as runTransformation } from '~/runTransformation'
export { runTransformation } from '~/runTransformation'
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"@types/jscodeshift": "^0.11.6",
"@types/yargs": "^17.0.24",
"cosmiconfig": "^8.3.4",
"cosmiconfig-typescript-loader": "^5.0.0",
"debug": "^4.1.1",
"fuzzy": "^0.1.3",
"globby": "^13.2.2",
Expand Down
66 changes: 43 additions & 23 deletions src/__tests__/bin.spec.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import fs from 'fs'
import path from 'path'

import { cosmiconfigSync } from 'cosmiconfig'
import { cosmiconfig } from 'cosmiconfig'
import { globbySync } from 'globby'
import { prompt } from 'inquirer'
import inquirer from 'inquirer'
import { vol } from 'memfs'

import { main } from '../bin'
import { loadModuleFromPath } from '../utils/loadModuleFromPath'

jest.mock('fs')
jest.mock('fs/promises')
jest.mock('../utils/loadModuleFromPath')

/* TEST UTILS */
async function runBinary(...args: string[]) {
Expand All @@ -32,12 +34,26 @@ describe('vue-sfcmod binary', () => {
const search = jest.fn()

beforeEach(() => {
loadModuleFromPath.mockImplementation((nameOrPath) => {
const customModulePath = path.resolve(process.cwd(), nameOrPath)
if (fs.existsSync(customModulePath)) {
const content = fs.readFileSync(customModulePath).toString('utf8')

// eslint-disable-next-line no-eval
return eval(content)
}

throw new Error(`Cannot find transformation module ${nameOrPath}`)
})

globbySync.mockImplementation((p) => (Array.isArray(p) ? p : [p]))
cosmiconfigSync.mockImplementation(() => {

cosmiconfig.mockImplementation(() => {
return {
search,
}
})

search.mockImplementation(() => {
return {
config: null,
Expand All @@ -47,6 +63,7 @@ describe('vue-sfcmod binary', () => {

consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(() => {})
consoleErrSpy = jest.spyOn(console, 'error').mockImplementation(() => {})

vol.reset()
})

Expand All @@ -68,7 +85,11 @@ describe('vue-sfcmod binary', () => {
})

it('fails when the transformation module path is wrong', async () => {
await expect(() => runBinary('Input.vue', '-t', 'foo')).rejects.toThrow(
vol.fromJSON({
'/tmp/Input.vue': '<template>Hello world</template><script></script>',
})

await expect(() => runBinary('/tmp/Input.vue', '-t', 'foo')).rejects.toThrow(
'Cannot find transformation module foo',
)
})
Expand All @@ -90,17 +111,16 @@ describe('vue-sfcmod binary', () => {
'module.exports = function(file, api, options) { return JSON.stringify(options) }',
})

await expect(() =>
runBinary(
'--foo',
'foo',
'-t',
'/tmp/transformation.cjs',
'--bar=bar',
'/tmp/Input.ts',
'--flag',
),
).not.toThrow()
await runBinary(
'--foo',
'foo',
'-t',
'/tmp/transformation.cjs',
'--bar=bar',
'/tmp/Input.ts',
'--flag',
)

expect(fs.readFileSync('/tmp/Input.ts').toString('utf-8')).toBe(
'{"foo":"foo","bar":"bar","flag":true}',
)
Expand Down Expand Up @@ -149,16 +169,16 @@ describe('vue-sfcmod binary', () => {
'/tmp/Input.vue': '<template>Hello world</template><script></script>',
'/tmp/transformation.cjs': 'module.exports = {}',
})
prompt.mockReturnValueOnce({ preset: '/tmp/transformation.cjs' })
inquirer.prompt.mockReturnValueOnce({ preset: '/tmp/transformation.cjs' })
search.mockImplementation(() => ({
config: { presets: ['/tmp/transformation.cjs'] },
filepath: 'mock-sfcmod.config.ts',
isEmpty: false,
}))

await expect(() => runBinary('/tmp/Input.vue')).not.toThrow()
expect(prompt).toHaveBeenCalled()
expect(prompt).toHaveReturnedWith(
expect(inquirer.prompt).toHaveBeenCalled()
expect(inquirer.prompt).toHaveReturnedWith(
expect.objectContaining({
preset: '/tmp/transformation.cjs',
}),
Expand All @@ -178,7 +198,7 @@ describe('vue-sfcmod binary', () => {
}
})

await expect(() => runBinary('Input.vue', '-t', 'foo')).rejects.toThrow(
await expect(() => runBinary('/tmp/Input.vue', '-t', 'foo')).rejects.toThrow(
'Cannot find transformation module foo',
)
})
Expand All @@ -188,7 +208,7 @@ describe('vue-sfcmod binary', () => {
'/tmp/Input.vue': '<template>Hello world</template><script></script>',
'/tmp/transformation.cjs': 'module.exports = {}',
})
prompt.mockReturnValueOnce({ preset: '/tmp/transformation.cjs' })
inquirer.prompt.mockReturnValueOnce({ preset: '/tmp/transformation.cjs' })
const name = jest.fn().mockReturnValue('test')
search.mockImplementation(() => ({
config: { presets: [{ glob: '/tmp/transformation.cjs', name }] },
Expand All @@ -197,8 +217,8 @@ describe('vue-sfcmod binary', () => {
}))

await expect(() => runBinary('/tmp/Input.vue')).not.toThrow()
expect(prompt).toHaveBeenCalled()
expect(prompt).toHaveReturnedWith(
expect(inquirer.prompt).toHaveBeenCalled()
expect(inquirer.prompt).toHaveReturnedWith(
expect.objectContaining({
preset: '/tmp/transformation.cjs',
}),
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/runTransformation.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-env jest */
import type { Transform } from 'jscodeshift'

import runTransformation from '../runTransformation'
import { runTransformation } from '../runTransformation'
import { TemplateTransformation } from '../types/TemplateTransformation'

const unreachableTransform: Transform = () => {
Expand Down
35 changes: 14 additions & 21 deletions src/bin.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as fs from 'fs'
import * as path from 'path'
import fs from 'fs'

import { cosmiconfigSync } from 'cosmiconfig'
import { cosmiconfig } from 'cosmiconfig'
import { TypeScriptLoader } from 'cosmiconfig-typescript-loader'
import createDebug from 'debug'
import fuzzy from 'fuzzy'
import { globbySync } from 'globby'
Expand All @@ -10,8 +10,9 @@ import inquirerPrompt from 'inquirer-autocomplete-prompt'
import yargs from 'yargs'

import { isValidConfig } from '~/config.schema'
import runTransformation from '~/runTransformation'
import { runTransformation } from '~/runTransformation'
import type { Options } from '~/types/TransformationOptions'
import { loadModuleFromPath } from '~/utils/loadModuleFromPath'

/* Find a transform to run and run it on input files. */
export async function main() {
Expand All @@ -20,8 +21,12 @@ export async function main() {
const debug = createDebug('vue-sfcmod')

/* Load config file. */
const explorerSync = cosmiconfigSync('sfcmod')
const configResult = explorerSync.search()
const explorer = cosmiconfig('sfcmod', {
loaders: {
'.ts': TypeScriptLoader(),
},
})
const configResult = await explorer.search()
let config
if (configResult === null) {
debug('No config file found.')
Expand Down Expand Up @@ -77,22 +82,10 @@ export async function main() {
}
}

/* Load arbitrary transformation module path. */
async function loadTransformationModule(nameOrPath: string) {
const customModulePath = path.resolve(process.cwd(), nameOrPath)
if (fs.existsSync(customModulePath)) {
const content = fs.readFileSync(customModulePath).toString('utf8')

// eslint-disable-next-line no-eval
return eval(content)
}

throw new Error(`Cannot find transformation module ${nameOrPath}`)
}

/* Load arbitrary transformation module path or ask the user if not found. */
let transformationModule
if (transformationName) {
transformationModule = await loadTransformationModule(transformationName)
transformationModule = await loadModuleFromPath(transformationName)
} else {
const answer = await inquirer.prompt({
// @ts-expect-error TS does not recognise `type: 'autocomplete'` which would be cumbersome to shim.
Expand All @@ -115,7 +108,7 @@ export async function main() {
},
})

transformationModule = await loadTransformationModule(answer.preset)
transformationModule = await loadModuleFromPath(answer.preset)
}

/* Start running. */
Expand Down
2 changes: 1 addition & 1 deletion src/runTransformation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import type { Options } from '~/types/TransformationOptions'
import debug from '~/utils/debug'
import { normaliseTransformationModule } from '~/utils/normaliseTransformationModule'

export default function runTransformation(
export function runTransformation(
fileInfo: FileInfo,
transformationModule: TransformationModule,
params: Options = {},
Expand Down
20 changes: 20 additions & 0 deletions src/utils/loadModuleFromPath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import fs from 'fs'
import path from 'path'

import type { TransformationModule } from '~/types/TransformationModule'
import debug from '~/utils/debug'

export function loadModuleFromPath(nameOrPath: string): Promise<TransformationModule> {
debug(`Loading transformation module ${nameOrPath}`)

const customModulePath = path.resolve(process.cwd(), nameOrPath)
debug(`Resolved to ${customModulePath}`)

if (fs.existsSync(customModulePath)) {
debug('Module exists on filesystem')

return import(customModulePath)
}

throw new Error(`Cannot find transformation module ${nameOrPath}`)
}
12 changes: 12 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2758,6 +2758,13 @@ cosmiconfig-typescript-loader@^4.0.0:
resolved "https://registry.yarnpkg.com/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.4.0.tgz#f3feae459ea090f131df5474ce4b1222912319f9"
integrity sha512-BabizFdC3wBHhbI4kJh0VkQP9GkBfoHPydD0COMce1nJ1kJAB3F2TmJ/I7diULBKtmEWSwEbuN/KDtgnmUUVmw==

cosmiconfig-typescript-loader@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-5.0.0.tgz#0d3becfe022a871f7275ceb2397d692e06045dc8"
integrity sha512-+8cK7jRAReYkMwMiG+bxhcNKiHJDM6bR9FD/nGBXOWdMLuYawjF5cGrtLilJ+LGd3ZjCXnJjR5DkfWPoIVlqJA==
dependencies:
jiti "^1.19.1"

cosmiconfig@^8.0.0:
version "8.2.0"
resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.2.0.tgz#f7d17c56a590856cd1e7cee98734dca272b0d8fd"
Expand Down Expand Up @@ -4643,6 +4650,11 @@ jest@^29.6.2:
import-local "^3.0.2"
jest-cli "^29.6.4"

jiti@^1.19.1:
version "1.20.0"
resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.20.0.tgz#2d823b5852ee8963585c8dd8b7992ffc1ae83b42"
integrity sha512-3TV69ZbrvV6U5DfQimop50jE9Dl6J8O1ja1dvBbMba/sZ3YBEQqJ2VZRoQPVnhlzjNtU1vaXRZVrVjU4qtm8yA==

js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
Expand Down

0 comments on commit c45ac70

Please sign in to comment.