From 034ab25d21986268687df2b51dcfd551bf4e0a43 Mon Sep 17 00:00:00 2001 From: Michael Loughry Date: Wed, 10 Apr 2019 23:26:56 -0700 Subject: [PATCH] Allow JSON transforms (#8278) * Allow JSON transforms for #2578 * Additional comments * A better fix, with test * Fix linting error from CI runs * Address comment from @scotthovestadt * Re-implement per @scotthovestadt's guidance * Update Changelog * Provide full options to transformJson * Fix lint error --- CHANGELOG.md | 1 + .../__tests__/runtime_internal_module.test.js | 24 +++++++++++++ .../__tests__/test_root/internal-root.json | 3 ++ .../test_root/test_json_preprocessor.js | 14 ++++++++ packages/jest-runtime/src/index.ts | 34 +++++++++++++------ .../jest-transform/src/ScriptTransformer.ts | 22 ++++++++++++ packages/jest-transform/src/index.ts | 6 +++- 7 files changed, 93 insertions(+), 11 deletions(-) create mode 100644 packages/jest-runtime/src/__tests__/test_root/internal-root.json create mode 100644 packages/jest-runtime/src/__tests__/test_root/test_json_preprocessor.js diff --git a/CHANGELOG.md b/CHANGELOG.md index e4f0e0dfa58d..e23216d3f0a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ### Fixes - `[jest-snapshot]` Inline snapshots: do not indent empty lines ([#8277](https://github.com/facebook/jest/pull/8277)) +- `[@jest/runtime, @jest/transform]` Allow custom transforms for JSON dependencies ([#2578](https://github.com/facebook/jest/pull/2578)) - `[jest-core]` Make `detectOpenHandles` imply `runInBand` ([#8283](https://github.com/facebook/jest/pull/8283)) - `[jest-haste-map]` Fix the `mapper` option which was incorrectly ignored ([#8299](https://github.com/facebook/jest/pull/8299)) diff --git a/packages/jest-runtime/src/__tests__/runtime_internal_module.test.js b/packages/jest-runtime/src/__tests__/runtime_internal_module.test.js index b434b0994804..7ed833b9f4a9 100644 --- a/packages/jest-runtime/src/__tests__/runtime_internal_module.test.js +++ b/packages/jest-runtime/src/__tests__/runtime_internal_module.test.js @@ -44,5 +44,29 @@ describe('Runtime', () => { const exports = runtime.requireInternalModule(modulePath); expect(exports()).toBe('internal-module-data'); })); + + it('loads JSON modules and applies transforms', () => + createRuntime(__filename, { + transform: {'^.+\\.json$': './test_json_preprocessor'}, + }).then(runtime => { + const modulePath = path.resolve( + path.dirname(runtime.__mockRootPath), + 'internal-root.json', + ); + const exports = runtime.requireModule(modulePath); + expect(exports).toEqual({foo: 'foo'}); + })); + + it('loads internal JSON modules without applying transforms', () => + createRuntime(__filename, { + transform: {'^.+\\.json$': './test_json_preprocessor'}, + }).then(runtime => { + const modulePath = path.resolve( + path.dirname(runtime.__mockRootPath), + 'internal-root.json', + ); + const exports = runtime.requireInternalModule(modulePath); + expect(exports).toEqual({foo: 'bar'}); + })); }); }); diff --git a/packages/jest-runtime/src/__tests__/test_root/internal-root.json b/packages/jest-runtime/src/__tests__/test_root/internal-root.json new file mode 100644 index 000000000000..c8c4105eb57c --- /dev/null +++ b/packages/jest-runtime/src/__tests__/test_root/internal-root.json @@ -0,0 +1,3 @@ +{ + "foo": "bar" +} diff --git a/packages/jest-runtime/src/__tests__/test_root/test_json_preprocessor.js b/packages/jest-runtime/src/__tests__/test_root/test_json_preprocessor.js new file mode 100644 index 000000000000..ec9b48f69cc2 --- /dev/null +++ b/packages/jest-runtime/src/__tests__/test_root/test_json_preprocessor.js @@ -0,0 +1,14 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +'use strict'; + +module.exports.process = source => { + const json = JSON.parse(source); + Object.keys(json).forEach(k => (json[k] = k)); + return JSON.stringify(json); +}; diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts index 9fbe5fdea215..917b47e377a4 100644 --- a/packages/jest-runtime/src/index.ts +++ b/packages/jest-runtime/src/index.ts @@ -25,6 +25,7 @@ import { ScriptTransformer, ShouldInstrumentOptions, shouldInstrument, + TransformationOptions, } from '@jest/transform'; import fs from 'graceful-fs'; import stripBOM from 'strip-bom'; @@ -465,8 +466,16 @@ class Runtime { moduleRegistry: ModuleRegistry, ) { if (path.extname(modulePath) === '.json') { + const text = stripBOM(fs.readFileSync(modulePath, 'utf8')); + + const transformedFile = this._scriptTransformer.transformJson( + modulePath, + this._getFullTransformationOptions(options), + text, + ); + localModule.exports = this._environment.global.JSON.parse( - stripBOM(fs.readFileSync(modulePath, 'utf8')), + transformedFile, ); } else if (path.extname(modulePath) === '.node') { localModule.exports = require(modulePath); @@ -478,6 +487,19 @@ class Runtime { localModule.loaded = true; } + private _getFullTransformationOptions( + options: InternalModuleOptions | undefined, + ): TransformationOptions { + return { + ...options, + changedFiles: this._coverageOptions.changedFiles, + collectCoverage: this._coverageOptions.collectCoverage, + collectCoverageFrom: this._coverageOptions.collectCoverageFrom, + collectCoverageOnlyFrom: this._coverageOptions.collectCoverageOnlyFrom, + extraGlobals: this._config.extraGlobals || [], + }; + } + requireModuleOrMock(from: Config.Path, moduleName: string) { try { if (this._shouldMock(from, moduleName)) { @@ -676,7 +698,6 @@ class Runtime { return; } - const isInternalModule = !!(options && options.isInternalModule); const filename = localModule.filename; const lastExecutingModulePath = this._currentlyExecutingModulePath; this._currentlyExecutingModulePath = filename; @@ -701,14 +722,7 @@ class Runtime { const extraGlobals = this._config.extraGlobals || []; const transformedFile = this._scriptTransformer.transform( filename, - { - changedFiles: this._coverageOptions.changedFiles, - collectCoverage: this._coverageOptions.collectCoverage, - collectCoverageFrom: this._coverageOptions.collectCoverageFrom, - collectCoverageOnlyFrom: this._coverageOptions.collectCoverageOnlyFrom, - extraGlobals, - isInternalModule, - }, + this._getFullTransformationOptions(options), this._cacheFS[filename], ); diff --git a/packages/jest-transform/src/ScriptTransformer.ts b/packages/jest-transform/src/ScriptTransformer.ts index e7267362c981..3e0e7c4d8b93 100644 --- a/packages/jest-transform/src/ScriptTransformer.ts +++ b/packages/jest-transform/src/ScriptTransformer.ts @@ -405,6 +405,28 @@ export default class ScriptTransformer { return result; } + transformJson( + filename: Config.Path, + options: Options, + fileSource: string, + ): string { + const isInternalModule = options.isInternalModule; + const isCoreModule = options.isCoreModule; + const willTransform = + !isInternalModule && !isCoreModule && this.shouldTransform(filename); + + if (willTransform) { + const {code: transformedJsonSource} = this.transformSource( + filename, + fileSource, + false, + ); + return transformedJsonSource; + } + + return fileSource; + } + /** * @deprecated use `this.shouldTransform` instead */ diff --git a/packages/jest-transform/src/index.ts b/packages/jest-transform/src/index.ts index 4a757f5ad9fd..c5be0650c891 100644 --- a/packages/jest-transform/src/index.ts +++ b/packages/jest-transform/src/index.ts @@ -7,4 +7,8 @@ export {default as ScriptTransformer} from './ScriptTransformer'; export {default as shouldInstrument} from './shouldInstrument'; -export {Transformer, ShouldInstrumentOptions} from './types'; +export { + Transformer, + ShouldInstrumentOptions, + Options as TransformationOptions, +} from './types';