Skip to content

Commit

Permalink
feat(jest-transform): add additionalTransformer
Browse files Browse the repository at this point in the history
  • Loading branch information
uhyo committed Feb 18, 2024
1 parent 9b4d246 commit e320624
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 49 deletions.
22 changes: 10 additions & 12 deletions packages/jest-transform/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,21 @@ Example Jest configuration:

`@nitrogql/jest-transform` is only capable of emitting ES modules. If you are running CommonJS modules during your tests, you will need to use another transformer (for example, `babel-jest` or `ts-jest`) to further transform the output from ES modules to CommonJS.

Using multiple transformers should be possible by using the [jest-chain-transform](https://www.npmjs.com/package/jest-chain-transform) package:
`@nitrogql/jest-transform` lets you apply another transformer to the output. To do so, use the `additionalTransformer` and `additionalTransformerFilenameSuffix` option:

```js
{
"transform": {
// For example, if you are using ts-jest...
"^.+\.tsx?": "ts-jest",
// Then, use jest-chain-transform to chain @nitrogql/jest-transform
// with ts-jest
"^.+\\.graphql$": ["jest-chain-transform", {
"transformers": [
["@nitrogql/jest-transform", {
"configFile": path.resolve(__dirname, "nitrogql.config.js")
}],
"ts-jest"
]
"^.+\.tsx?": ["ts-jest", { isolatedModules: true }],
"^.+\\.graphql$": ["@nitrogql/jest-transform", {
"configFile": path.resolve(__dirname, "nitrogql.config.yml"),
// Then, use the additionalTransformer option to apply ts-jest to the output.
"additionalTransformer": ["ts-jest", { isolatedModules: true }],
// ts-jest expects .ts files, so we need to change the file extension
// by applying the additionalTransformerFilenameSuffix option.
"additionalTransformerFilenameSuffix": ".ts"
}]
}
},
}
```
135 changes: 98 additions & 37 deletions packages/jest-transform/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,118 @@
import { readFileSync } from "node:fs";
import type { SyncTransformer, TransformedSource } from "@jest/transform";
import type {
SyncTransformer,
TransformedSource,
TransformerCreator,
} from "@jest/transform";
import { executeConfigFileSync } from "@nitrogql/core";
import { init } from "@nitrogql/loader-core";

const { initiateTask, getLog } = await init();

let lastLoadedConfigPath: string | undefined = undefined;

export type TransformerConfig = {
/**
* The path to the nitrogql config file.
*/
configFile?: string;
/**
* Additional transformer to apply to the generated source code.
*/
additionalTransformer?:
| string
| [transformer: string, transformerConfig: unknown];
/**
* Suffix to add to filename when passing code to the additional transformer.
* @default ".js"
*/
additionalTransformerFilenameSuffix?: string;
};

const transformer: SyncTransformer<TransformerConfig> = {
process(sourceText, sourcePath, options): TransformedSource {
const configFile = options.transformerConfig.configFile;
const task = initiateTask(sourcePath, sourceText);

if (lastLoadedConfigPath !== configFile && configFile) {
const configFileSource = configFileIsJS(configFile)
? executeConfigFileSync(configFile)
: readFileSync(configFile, "utf-8");
task.loadConfig(configFileSource);
}
lastLoadedConfigPath = configFile;

while (true) {
const status = task.status();
switch (status.status) {
case "fileRequired": {
const requiredFiles = status.files;
for (const requiredFile of requiredFiles) {
const requiredFileSource = readFileSync(requiredFile, "utf-8");
task.supplyFile(requiredFile, requiredFileSource);
const createTransformer: TransformerCreator<
SyncTransformer<TransformerConfig>,
TransformerConfig
> = async (options) => {
const { initiateTask } = await init();

let lastLoadedConfigPath: string | undefined = undefined;

const additionalTransformer = options?.additionalTransformer
? await loadTransformer(options.additionalTransformer)
: undefined;

const transformer: SyncTransformer<TransformerConfig> = {
process(sourceText, sourcePath, options): TransformedSource {
const configFile = options.transformerConfig.configFile;
const task = initiateTask(sourcePath, sourceText);

if (lastLoadedConfigPath !== configFile && configFile) {
const configFileSource = configFileIsJS(configFile)
? executeConfigFileSync(configFile)
: readFileSync(configFile, "utf-8");
task.loadConfig(configFileSource);
}
lastLoadedConfigPath = configFile;

let result: string;
loop: while (true) {
const status = task.status();
switch (status.status) {
case "fileRequired": {
const requiredFiles = status.files;
for (const requiredFile of requiredFiles) {
const requiredFileSource = readFileSync(requiredFile, "utf-8");
task.supplyFile(requiredFile, requiredFileSource);
}
break;
}
case "ready": {
result = task.emit();
task.free();
break loop;
}
break;
}
case "ready": {
const result = task.emit();
task.free();
return {
code: result,
};
}
}
}
},
if (additionalTransformer === undefined) {
return { code: result };
}
const transformed = additionalTransformer[0].process(
result,
sourcePath +
(options.transformerConfig.additionalTransformerFilenameSuffix ??
".js"),
{
...options,
transformerConfig: additionalTransformer[1],
}
);
return transformed;
},
};
return transformer;
};

export default transformer;
export default {
createTransformer,
};

function configFileIsJS(configFile: string) {
return /\.[cm]?[jt]s$/.test(configFile);
}

async function loadTransformer(
transformer: string | [transformer: string, transformerConfig: unknown]
): Promise<[transformer: SyncTransformer<unknown>, config: unknown]> {
let transformerModule, transformerConfig: unknown;
if (typeof transformer === "string") {
transformerModule = await import(transformer);
} else {
transformerModule = await import(transformer[0]);
transformerConfig = transformer[1];
}
while (transformerModule.default) {
transformerModule = transformerModule.default;
}
if (transformerModule.createTransformer) {
transformerModule = await transformerModule.createTransformer(
transformerConfig
);
}
return [transformerModule, transformerConfig];
}

0 comments on commit e320624

Please sign in to comment.