Files
cypress/packages/server/lib/plugins/preprocessor.js
Jim Hays 34c8b1f884 refactor: Spellcheck (#27964)
Co-authored-by: Matthew Schile <mschile@cypress.io>
2023-10-11 10:06:10 -04:00

150 lines
3.4 KiB
JavaScript

require('../cwd')
const _ = require('lodash')
const EE = require('events')
const path = require('path')
const debug = require('debug')('cypress:server:preprocessor')
const Promise = require('bluebird')
const appData = require('../util/app_data')
const plugins = require('../plugins')
const { telemetry } = require('@packages/telemetry')
const errorMessage = function (err = {}) {
return err.stack || err.annotated || err.message || err.toString()
}
const clientSideError = function (err) {
// eslint-disable-next-line no-console
console.log(err.message)
err = errorMessage(err)
return `\
(function () {
Cypress.action("spec:script:error", {
type: "BUNDLE_ERROR",
error: ${JSON.stringify(err)}
})
}())\
`
}
const baseEmitter = new EE()
let fileObjects = {}
let fileProcessors = {}
plugins.registerHandler((ipc) => {
ipc.on('preprocessor:rerun', (filePath) => {
debug('ipc preprocessor:rerun event')
baseEmitter.emit('file:updated', filePath)
})
baseEmitter.on('close', (filePath) => {
debug('base emitter plugin close event')
ipc.send('preprocessor:close', filePath)
})
})
// for simpler stubbing from unit tests
const API = {
errorMessage,
clientSideError,
emitter: baseEmitter,
getFile (filePath, config) {
let fileObject; let fileProcessor
debug(`getting file ${filePath}`)
filePath = path.resolve(config.projectRoot, filePath)
debug(`getFile ${filePath}`)
if (!(fileObject = fileObjects[filePath])) {
// we should be watching the file if we are NOT
// in a text terminal aka cypress run
// TODO: rename this to config.isRunMode
// vs config.isInteractiveMode
const shouldWatch = !config.isTextTerminal || Boolean(process.env.CYPRESS_INTERNAL_FORCE_FILEWATCH)
const baseFilePath = filePath.replace(config.projectRoot, '')
fileObject = (fileObjects[filePath] = _.extend(new EE(), {
filePath,
shouldWatch,
outputPath: appData.getBundledFilePath(config.projectRoot, baseFilePath),
}))
fileObject.on('rerun', () => {
debug('file object rerun event')
return baseEmitter.emit('file:updated', filePath)
})
baseEmitter.once('close', () => {
debug('base emitter native close event')
return fileObject.emit('close')
})
}
if (config.isTextTerminal && (fileProcessor = fileProcessors[filePath])) {
debug('headless and already processed')
return fileProcessor
}
const preprocessor = (fileProcessors[filePath] = Promise.try(() => {
const span = telemetry.startSpan({ name: 'file:preprocessor' })
return plugins.execute('file:preprocessor', fileObject).then((arg) => {
span?.setAttribute('file', arg)
span?.end()
return arg
})
}))
return preprocessor
},
removeFile (filePath, config) {
let fileObject
filePath = path.resolve(config.projectRoot, filePath)
if (!fileProcessors[filePath]) {
return
}
debug(`removeFile ${filePath}`)
baseEmitter.emit('close', filePath)
fileObject = fileObjects[filePath]
if (fileObject) {
fileObject.emit('close')
}
delete fileObjects[filePath]
delete fileProcessors[filePath]
},
close () {
debug('close preprocessor')
fileObjects = {}
fileProcessors = {}
baseEmitter.emit('close')
baseEmitter.removeAllListeners()
},
}
module.exports = API