mirror of
https://github.com/cypress-io/cypress.git
synced 2026-03-10 02:28:52 -05:00
fix: Resolve tsconfig.json for plugins file from the plugins directory (#8377)
This commit is contained in:
@@ -157,7 +157,7 @@ const execute = (ipc, event, ids, args = []) => {
|
||||
|
||||
let tsRegistered = false
|
||||
|
||||
module.exports = (ipc, pluginsFile, projectRoot) => {
|
||||
const runPlugins = (ipc, pluginsFile, projectRoot) => {
|
||||
debug('pluginsFile:', pluginsFile)
|
||||
debug('project root:', projectRoot)
|
||||
if (!projectRoot) {
|
||||
@@ -181,7 +181,7 @@ module.exports = (ipc, pluginsFile, projectRoot) => {
|
||||
})
|
||||
|
||||
if (!tsRegistered) {
|
||||
registerTsNode(projectRoot)
|
||||
registerTsNode(projectRoot, pluginsFile)
|
||||
|
||||
// ensure typescript is only registered once
|
||||
tsRegistered = true
|
||||
@@ -219,3 +219,10 @@ module.exports = (ipc, pluginsFile, projectRoot) => {
|
||||
execute(ipc, event, ids, args)
|
||||
})
|
||||
}
|
||||
|
||||
// for testing purposes
|
||||
runPlugins.__reset = () => {
|
||||
tsRegistered = false
|
||||
}
|
||||
|
||||
module.exports = runPlugins
|
||||
|
||||
@@ -7,7 +7,7 @@ const debug = require('debug')('cypress:server:preprocessor')
|
||||
const Promise = require('bluebird')
|
||||
const appData = require('../util/app_data')
|
||||
const plugins = require('../plugins')
|
||||
const resolve = require('./resolve')
|
||||
const resolve = require('../util/resolve')
|
||||
|
||||
const errorMessage = function (err = {}) {
|
||||
return (err.stack || err.annotated || err.message || err.toString())
|
||||
@@ -46,8 +46,7 @@ const createPreprocessor = function (options) {
|
||||
const setDefaultPreprocessor = function (config) {
|
||||
debug('set default preprocessor')
|
||||
|
||||
const tsPath = resolve.typescript(config)
|
||||
|
||||
const tsPath = resolve.typescript(config.projectRoot)
|
||||
const options = {
|
||||
typescript: tsPath,
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ const keys = require('./util/keys')
|
||||
const settings = require('./util/settings')
|
||||
const specsUtil = require('./util/specs')
|
||||
const { escapeFilenameInUrl } = require('./util/escape_filename')
|
||||
const { registerTsNode } = require('./util/ts-node')
|
||||
|
||||
const localCwd = cwd()
|
||||
|
||||
@@ -100,10 +99,6 @@ class Project extends EE {
|
||||
|
||||
return scaffold.plugins(path.dirname(cfg.pluginsFile), cfg)
|
||||
}
|
||||
}).then((cfg) => {
|
||||
registerTsNode(this.projectRoot)
|
||||
|
||||
return cfg
|
||||
}).then((cfg) => {
|
||||
return this._initPlugins(cfg, options)
|
||||
.then((modifiedCfg) => {
|
||||
|
||||
@@ -1,26 +1,22 @@
|
||||
const resolve = require('resolve')
|
||||
const env = require('../util/env')
|
||||
const env = require('./env')
|
||||
const debug = require('debug')('cypress:server:plugins')
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* Resolves the path to 'typescript' module.
|
||||
*
|
||||
* @param {Config} cypress config object
|
||||
* @param {projectRoot} path to the project root
|
||||
* @returns {string|null} path if typescript exists, otherwise null
|
||||
*/
|
||||
typescript: (config) => {
|
||||
if (env.get('CYPRESS_INTERNAL_NO_TYPESCRIPT') === '1') {
|
||||
typescript: (projectRoot) => {
|
||||
if (env.get('CYPRESS_INTERNAL_NO_TYPESCRIPT') === '1' || !projectRoot) {
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
const options = {
|
||||
basedir: config.projectRoot,
|
||||
}
|
||||
|
||||
if (!config.projectRoot) {
|
||||
throw new Error('Config is missing projet root')
|
||||
basedir: projectRoot,
|
||||
}
|
||||
|
||||
debug('resolving typescript with options %o', options)
|
||||
@@ -1,23 +1,28 @@
|
||||
const debug = require('debug')('cypress:server:ts-node')
|
||||
const path = require('path')
|
||||
const tsnode = require('ts-node')
|
||||
const resolve = require('resolve')
|
||||
const resolve = require('./resolve')
|
||||
|
||||
const getTsNodeOptions = (tsPath) => {
|
||||
const getTsNodeOptions = (tsPath, pluginsFile) => {
|
||||
return {
|
||||
compiler: tsPath, // use the user's installed typescript
|
||||
compilerOptions: {
|
||||
module: 'CommonJS',
|
||||
},
|
||||
// resolves tsconfig.json starting from the plugins directory
|
||||
// instead of the cwd (the project root)
|
||||
dir: path.dirname(pluginsFile),
|
||||
transpileOnly: true, // transpile only (no type-check) for speed
|
||||
}
|
||||
}
|
||||
|
||||
const registerTsNode = (projectRoot) => {
|
||||
const registerTsNode = (projectRoot, pluginsFile) => {
|
||||
try {
|
||||
const tsPath = resolve.sync('typescript', {
|
||||
basedir: projectRoot,
|
||||
})
|
||||
const tsOptions = getTsNodeOptions(tsPath)
|
||||
const tsPath = resolve.typescript(projectRoot)
|
||||
|
||||
if (!tsPath) return
|
||||
|
||||
const tsOptions = getTsNodeOptions(tsPath, pluginsFile)
|
||||
|
||||
debug('typescript path: %s', tsPath)
|
||||
debug('registering project TS with options %o', tsOptions)
|
||||
|
||||
@@ -23,4 +23,11 @@ describe('e2e typescript in plugins file', function () {
|
||||
project: Fixtures.projectPath('ts-proj-esmoduleinterop-true'),
|
||||
})
|
||||
})
|
||||
|
||||
// https://github.com/cypress-io/cypress/issues/8359
|
||||
it('loads tsconfig.json from plugins directory', function () {
|
||||
return e2e.exec(this, {
|
||||
project: Fixtures.projectPath('ts-proj-tsconfig-in-plugins'),
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -23,7 +23,7 @@ const Project = require(`${root}lib/project`)
|
||||
const Watchers = require(`${root}lib/watchers`)
|
||||
const pluginsModule = require(`${root}lib/plugins`)
|
||||
const preprocessor = require(`${root}lib/plugins/preprocessor`)
|
||||
const resolve = require(`${root}lib/plugins/resolve`)
|
||||
const resolve = require(`${root}lib/util/resolve`)
|
||||
const fs = require(`${root}lib/util/fs`)
|
||||
const glob = require(`${root}lib/util/glob`)
|
||||
const CacheBuster = require(`${root}lib/util/cache_buster`)
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"supportFile": false
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
it('passes', () => {
|
||||
expect(true).to.be.true
|
||||
})
|
||||
@@ -0,0 +1,6 @@
|
||||
// this tests that the tsconfig.json is loaded from the plugins directory.
|
||||
// if it isn't, the lack of "downlevelIteration" support will cause this to
|
||||
// fail at runtime with "RangeError: Invalid array length"
|
||||
[...Array(100).keys()].map((x) => `${x}`)
|
||||
|
||||
export default () => {}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"downlevelIteration": true
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/// <reference types="cypress" />
|
||||
|
||||
import fn from './commonjs-export-function'
|
||||
import * as fn from './commonjs-export-function'
|
||||
|
||||
// if esModuleInterop is forced to be true, this will error // with 'fn is
|
||||
// not a function'. instead, we allow the tsconfig.json to determine the value
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -2,11 +2,13 @@ require('../../../spec_helper')
|
||||
|
||||
const _ = require('lodash')
|
||||
const snapshot = require('snap-shot-it')
|
||||
const tsnode = require('ts-node')
|
||||
|
||||
const preprocessor = require(`${root}../../lib/plugins/child/preprocessor`)
|
||||
const task = require(`${root}../../lib/plugins/child/task`)
|
||||
const runPlugins = require(`${root}../../lib/plugins/child/run_plugins`)
|
||||
const util = require(`${root}../../lib/plugins/util`)
|
||||
const resolve = require(`${root}../../lib/util/resolve`)
|
||||
const browserUtils = require(`${root}../../lib/browsers/utils`)
|
||||
const Fixtures = require(`${root}../../test/support/helpers/fixtures`)
|
||||
|
||||
@@ -31,8 +33,7 @@ describe('lib/plugins/child/run_plugins', () => {
|
||||
|
||||
afterEach(() => {
|
||||
mockery.deregisterMock('plugins-file')
|
||||
|
||||
return mockery.deregisterSubstitute('plugins-file')
|
||||
mockery.deregisterSubstitute('plugins-file')
|
||||
})
|
||||
|
||||
it('sends error message if pluginsFile is missing', function () {
|
||||
@@ -77,6 +78,43 @@ describe('lib/plugins/child/run_plugins', () => {
|
||||
return snapshot(JSON.stringify(this.ipc.send.lastCall.args[3]))
|
||||
})
|
||||
|
||||
describe('typescript registration', () => {
|
||||
beforeEach(function () {
|
||||
runPlugins.__reset()
|
||||
|
||||
this.register = sinon.stub(tsnode, 'register')
|
||||
sinon.stub(resolve, 'typescript').returns('/path/to/typescript.js')
|
||||
})
|
||||
|
||||
it('registers ts-node if typescript is installed', function () {
|
||||
runPlugins(this.ipc, '/path/to/plugins/file.js', 'proj-root')
|
||||
|
||||
expect(this.register).to.be.calledWith({
|
||||
transpileOnly: true,
|
||||
compiler: '/path/to/typescript.js',
|
||||
dir: '/path/to/plugins',
|
||||
compilerOptions: {
|
||||
module: 'CommonJS',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('only registers ts-node once', function () {
|
||||
runPlugins(this.ipc, '/path/to/plugins/file.js', 'proj-root')
|
||||
runPlugins(this.ipc, '/path/to/plugins/file.js', 'proj-root')
|
||||
|
||||
expect(this.register).to.be.calledOnce
|
||||
})
|
||||
|
||||
it('does not register ts-node if typescript is not installed', function () {
|
||||
resolve.typescript.returns(null)
|
||||
|
||||
runPlugins(this.ipc, '/path/to/plugins/file.js', 'proj-root')
|
||||
|
||||
expect(this.register).not.to.be.called
|
||||
})
|
||||
})
|
||||
|
||||
describe('on \'load\' message', () => {
|
||||
it('sends error if pluginsFile function rejects the promise', function (done) {
|
||||
const err = new Error('foo')
|
||||
|
||||
@@ -3,7 +3,6 @@ require('../spec_helper')
|
||||
const mockedEnv = require('mocked-env')
|
||||
const path = require('path')
|
||||
const commitInfo = require('@cypress/commit-info')
|
||||
const tsnode = require('ts-node')
|
||||
const Fixtures = require('../support/helpers/fixtures')
|
||||
const api = require(`${root}lib/api`)
|
||||
const user = require(`${root}lib/user`)
|
||||
@@ -316,57 +315,6 @@ This option will not have an effect in Some-other-name. Tests that rely on web s
|
||||
expect(config).ok
|
||||
})
|
||||
})
|
||||
|
||||
describe('out-of-the-box typescript setup', () => {
|
||||
const tsProjPath = Fixtures.projectPath('ts-installed')
|
||||
// Root path is used because resolve finds server typescript path when we use a project under `suppert/projects` folder.
|
||||
const rootPath = path.join(__dirname, '../../../../..')
|
||||
const projTsPath = path.join(tsProjPath, 'node_modules/typescript/index.js')
|
||||
|
||||
let cfg
|
||||
|
||||
beforeEach(() => {
|
||||
return config.get(tsProjPath, {})
|
||||
.then((c) => {
|
||||
cfg = c
|
||||
})
|
||||
})
|
||||
|
||||
const setupProject = (typescript, projectRoot) => {
|
||||
const proj = new Project(projectRoot)
|
||||
|
||||
sinon.stub(proj, 'watchSettingsAndStartWebsockets').resolves()
|
||||
sinon.stub(proj, 'checkSupportFile').resolves()
|
||||
sinon.stub(proj, 'scaffold').resolves()
|
||||
sinon.stub(proj, 'getConfig').resolves({ ...cfg, typescript })
|
||||
|
||||
const register = sinon.stub(tsnode, 'register')
|
||||
|
||||
return { proj, register }
|
||||
}
|
||||
|
||||
it('ts installed', () => {
|
||||
const { proj, register } = setupProject('default', tsProjPath)
|
||||
|
||||
return proj.open().then(() => {
|
||||
expect(register).to.be.calledWith({
|
||||
transpileOnly: true,
|
||||
compiler: projTsPath,
|
||||
compilerOptions: {
|
||||
module: 'CommonJS',
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('ts not installed', () => {
|
||||
const { proj, register } = setupProject('default', rootPath)
|
||||
|
||||
return proj.open().then(() => {
|
||||
expect(register).not.called
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('#close', () => {
|
||||
|
||||
Reference in New Issue
Block a user