Skip to content

Commit

Permalink
Add tests about ESM config resolution
Browse files Browse the repository at this point in the history
  • Loading branch information
yhatt committed Jul 8, 2023
1 parent 3b12431 commit 22813c1
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 6 deletions.
4 changes: 4 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ module.exports = {
'\\.s?css$': '<rootDir>/test/_transformers/css.js',
'\\.png$': '<rootDir>/test/_transformers/png.js',
'\\.pug$': '<rootDir>/test/_transformers/pug.js',

// TODO: Remove if Jest did not fail on ESM dynamic imports
'custom-engine\\.mjs$': 'babel-jest',
'config\\.mjs$': 'babel-jest',
'esm-project/marp\\.config\\.js$': 'babel-jest',
},
transformIgnorePatterns: [`/node_modules/(?!${esModules.join('|')})`],
testEnvironment: 'node',
Expand Down
18 changes: 12 additions & 6 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
import fs from 'fs'
import path from 'path'
import chalk from 'chalk'
import { cosmiconfig, cosmiconfigSync } from 'cosmiconfig'
import {
cosmiconfig,
cosmiconfigSync,
Options as CosmiconfigOptions,
} from 'cosmiconfig'
import { osLocale } from 'os-locale'
import { info, warn, error as cliError } from './cli'
import { ConverterOption, ConvertType } from './converter'
Expand Down Expand Up @@ -110,6 +114,10 @@ export class MarpCLIConfig {
return conf
}

static isESMAvailable() {
return ResolvedEngine.isESMAvailable()
}

private constructor() {} // eslint-disable-line @typescript-eslint/no-empty-function

async converterOption(): Promise<ConverterOption> {
Expand Down Expand Up @@ -331,11 +339,9 @@ export class MarpCLIConfig {
}

private async loadConf(confPath?: string) {
const generateCosmiconfigExplorer = ResolvedEngine.isESMAvailable()
? cosmiconfig
: cosmiconfigSync // sync version is using `require()` instead of `import()` so not expect to meet a trouble with ESM

const explorer = generateCosmiconfigExplorer(MarpCLIConfig.moduleName)
const explorer = MarpCLIConfig.isESMAvailable()
? cosmiconfig(MarpCLIConfig.moduleName)
: cosmiconfigSync(MarpCLIConfig.moduleName)

try {
const ret = await (confPath === undefined
Expand Down
25 changes: 25 additions & 0 deletions test/__mocks__/cosmiconfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const cosmiconfig: typeof import('cosmiconfig') =
jest.requireActual('cosmiconfig')

const {
cosmiconfig: originalCosmiconfig,
defaultLoaders,
defaultLoadersSync,
} = cosmiconfig

// Because of v8's bug, Jest fails with SIGSEGV if used dynamic import.
// When using ESM in tests, you have to set up Jest to transpile config files into CommonJS with Babel.
cosmiconfig.cosmiconfig = jest.fn((moduleName, options) => {
return originalCosmiconfig(moduleName, {
loaders: {
// cosmiconfig sync loader is using `require()` to load JS
...defaultLoaders,
'.js': defaultLoadersSync['.js'],
'.mjs': defaultLoadersSync['.js'],
'.cjs': defaultLoadersSync['.js'],
},
...(options ?? {}),
})
})

module.exports = cosmiconfig
6 changes: 6 additions & 0 deletions test/_configs/esm-project/marp.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/** @type {import('../../../src/index').Config} */
const config = {}

export default config

console.debug('A config file with .mjs extension was loaded.')
4 changes: 4 additions & 0 deletions test/_configs/esm-project/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"private": true,
"type": "module"
}
3 changes: 3 additions & 0 deletions test/_configs/mjs/config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default {}

console.debug('A config file with .mjs extension was loaded.')
43 changes: 43 additions & 0 deletions test/marp-cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const runForObservation = async (argv: string[]) => {
return ret
}

jest.mock('cosmiconfig')
jest.mock('fs')
jest.mock('../src/preview')
jest.mock('../src/watcher', () => jest.createMockFromModule('../src/watcher'))
Expand Down Expand Up @@ -1110,6 +1111,48 @@ describe('Marp CLI', () => {
}
})
})

describe('with ES Module', () => {
it('allows loading config with mjs extension', async () => {
const debug = jest.spyOn(console, 'debug').mockImplementation()
const log = jest.spyOn(console, 'log').mockImplementation()

try {
expect(
await marpCli(['-v', '-c', assetFn('_configs/mjs/config.mjs')])
).toBe(0)

expect(debug).toHaveBeenCalledWith(
expect.stringContaining('loaded')
)
} finally {
debug.mockRestore()
log.mockRestore()
}
})

it('allows loading config from ESM project', async () => {
const debug = jest.spyOn(console, 'debug').mockImplementation()
const log = jest.spyOn(console, 'log').mockImplementation()

try {
expect(
await marpCli([
'-v',
'-c',
assetFn('_configs/esm-project/marp.config.js'),
])
).toBe(0)

expect(debug).toHaveBeenCalledWith(
expect.stringContaining('loaded')
)
} finally {
debug.mockRestore()
log.mockRestore()
}
})
})
})

describe('with --preview / -p option', () => {
Expand Down

0 comments on commit 22813c1

Please sign in to comment.