refactor: init cypress-cucumber-preprocessor install.
This commit is contained in:
309
node_modules/@cypress/browserify-preprocessor/index.js
generated
vendored
Normal file
309
node_modules/@cypress/browserify-preprocessor/index.js
generated
vendored
Normal file
@@ -0,0 +1,309 @@
|
||||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
const Promise = require('bluebird')
|
||||
const fs = require('fs-extra')
|
||||
|
||||
const cloneDeep = require('lodash.clonedeep')
|
||||
const browserify = require('browserify')
|
||||
const watchify = require('watchify')
|
||||
|
||||
const debug = require('debug')('cypress:browserify')
|
||||
|
||||
const typescriptExtensionRegex = /\.tsx?$/
|
||||
const errorTypes = {
|
||||
TYPESCRIPT_AND_TSIFY: 'TYPESCRIPT_AND_TSIFY',
|
||||
TYPESCRIPT_NONEXISTENT: 'TYPESCRIPT_NONEXISTENT',
|
||||
TYPESCRIPT_NOT_CONFIGURED: 'TYPESCRIPT_NOT_CONFIGURED',
|
||||
TYPESCRIPT_NOT_STRING: 'TYPESCRIPT_NOT_STRING',
|
||||
}
|
||||
|
||||
const bundles = {}
|
||||
|
||||
// by default, we transform JavaScript (including some proposal features),
|
||||
// JSX, & CoffeeScript
|
||||
const defaultOptions = {
|
||||
browserifyOptions: {
|
||||
extensions: ['.js', '.jsx', '.coffee'],
|
||||
transform: [
|
||||
[
|
||||
require.resolve('coffeeify'),
|
||||
{},
|
||||
],
|
||||
[
|
||||
require.resolve('babelify'),
|
||||
{
|
||||
ast: false,
|
||||
babelrc: false,
|
||||
plugins: [
|
||||
...[
|
||||
'babel-plugin-add-module-exports',
|
||||
'@babel/plugin-proposal-class-properties',
|
||||
'@babel/plugin-proposal-object-rest-spread',
|
||||
].map(require.resolve),
|
||||
[require.resolve('@babel/plugin-transform-runtime'), {
|
||||
absoluteRuntime: path.dirname(require.resolve('@babel/runtime/package')),
|
||||
}],
|
||||
],
|
||||
presets: [
|
||||
'@babel/preset-env',
|
||||
'@babel/preset-react',
|
||||
].map(require.resolve),
|
||||
},
|
||||
],
|
||||
],
|
||||
plugin: [],
|
||||
},
|
||||
watchifyOptions: {
|
||||
// ignore watching the following or the user's system can get bogged down
|
||||
// by watchers
|
||||
ignoreWatch: [
|
||||
'**/.git/**',
|
||||
'**/.nyc_output/**',
|
||||
'**/.sass-cache/**',
|
||||
'**/bower_components/**',
|
||||
'**/coverage/**',
|
||||
'**/node_modules/**',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
const throwError = ({ message, type }) => {
|
||||
const prefix = 'Error running @cypress/browserify-preprocessor:\n\n'
|
||||
|
||||
const err = new Error(`${prefix}${message}`)
|
||||
|
||||
if (type) err.type = type
|
||||
|
||||
throw err
|
||||
}
|
||||
|
||||
const getBrowserifyOptions = async (entry, userBrowserifyOptions = {}, typescriptPath = null) => {
|
||||
let browserifyOptions = cloneDeep(defaultOptions.browserifyOptions)
|
||||
|
||||
// allow user to override default options
|
||||
browserifyOptions = Object.assign(browserifyOptions, userBrowserifyOptions, {
|
||||
// these must always be new objects or 'update' events will not fire
|
||||
cache: {},
|
||||
packageCache: {},
|
||||
})
|
||||
|
||||
// unless user has explicitly turned off source map support, always enable it
|
||||
// so we can use it to point user to the source code
|
||||
if (userBrowserifyOptions.debug !== false) {
|
||||
browserifyOptions.debug = true
|
||||
}
|
||||
|
||||
// we need to override and control entries
|
||||
Object.assign(browserifyOptions, {
|
||||
entries: [entry],
|
||||
})
|
||||
|
||||
if (typescriptPath) {
|
||||
if (typeof typescriptPath !== 'string') {
|
||||
throwError({
|
||||
type: errorTypes.TYPESCRIPT_NOT_STRING,
|
||||
message: `The 'typescript' option must be a string. You passed: ${typescriptPath}`,
|
||||
})
|
||||
}
|
||||
|
||||
const pathExists = await fs.pathExists(typescriptPath)
|
||||
|
||||
if (!pathExists) {
|
||||
throwError({
|
||||
type: errorTypes.TYPESCRIPT_NONEXISTENT,
|
||||
message: `The 'typescript' option must be a valid path to your TypeScript installation. We could not find anything at the following path: ${typescriptPath}`,
|
||||
})
|
||||
}
|
||||
|
||||
const transform = browserifyOptions.transform
|
||||
const hasTsifyTransform = transform.some((stage) => Array.isArray(stage) && stage[0].includes('tsify'))
|
||||
const hastsifyPlugin = browserifyOptions.plugin.includes('tsify')
|
||||
|
||||
if (hasTsifyTransform || hastsifyPlugin) {
|
||||
const type = hasTsifyTransform ? 'transform' : 'plugin'
|
||||
|
||||
throwError({
|
||||
type: errorTypes.TYPESCRIPT_AND_TSIFY,
|
||||
message: `It looks like you passed the 'typescript' option and also specified a browserify ${type} for TypeScript. This may cause conflicts.
|
||||
|
||||
Please do one of the following:
|
||||
|
||||
1) Pass in the 'typescript' option and omit the browserify ${type} (Recommmended)
|
||||
2) Omit the 'typescript' option and continue to use your own browserify ${type}`,
|
||||
})
|
||||
}
|
||||
|
||||
browserifyOptions.extensions.push('.ts', '.tsx')
|
||||
// remove babelify setting
|
||||
browserifyOptions.transform = transform.filter((stage) => !Array.isArray(stage) || !stage[0].includes('babelify'))
|
||||
// add typescript compiler
|
||||
browserifyOptions.transform.push([
|
||||
path.join(__dirname, './lib/simple_tsify'), {
|
||||
typescript: require(typescriptPath),
|
||||
},
|
||||
])
|
||||
}
|
||||
|
||||
debug('browserifyOptions: %o', browserifyOptions)
|
||||
|
||||
return browserifyOptions
|
||||
}
|
||||
|
||||
// export a function that returns another function, making it easy for users
|
||||
// to configure like so:
|
||||
//
|
||||
// on('file:preprocessor', browserify(options))
|
||||
//
|
||||
const preprocessor = (options = {}) => {
|
||||
debug('received user options: %o', options)
|
||||
|
||||
// we return function that accepts the arguments provided by
|
||||
// the event 'file:preprocessor'
|
||||
//
|
||||
// this function will get called for the support file when a project is loaded
|
||||
// (if the support file is not disabled)
|
||||
// it will also get called for a spec file when that spec is requested by
|
||||
// the Cypress runner
|
||||
//
|
||||
// when running in the GUI, it will likely get called multiple times
|
||||
// with the same filePath, as the user could re-run the tests, causing
|
||||
// the supported file and spec file to be requested again
|
||||
return async (file) => {
|
||||
const filePath = file.filePath
|
||||
|
||||
debug('get:', filePath)
|
||||
|
||||
// since this function can get called multiple times with the same
|
||||
// filePath, we return the cached bundle promise if we already have one
|
||||
// since we don't want or need to re-initiate browserify/watchify for it
|
||||
if (bundles[filePath]) {
|
||||
debug('already have bundle for:', filePath)
|
||||
|
||||
return bundles[filePath]
|
||||
}
|
||||
|
||||
// we're provided a default output path that lives alongside Cypress's
|
||||
// app data files so we don't have to worry about where to put the bundled
|
||||
// file on disk
|
||||
const outputPath = file.outputPath
|
||||
|
||||
debug('input:', filePath)
|
||||
debug('output:', outputPath)
|
||||
|
||||
const browserifyOptions = await getBrowserifyOptions(filePath, options.browserifyOptions, options.typescript)
|
||||
const watchifyOptions = Object.assign({}, defaultOptions.watchifyOptions, options.watchifyOptions)
|
||||
|
||||
if (!options.typescript && typescriptExtensionRegex.test(filePath)) {
|
||||
throwError({
|
||||
type: errorTypes.TYPESCRIPT_NOT_CONFIGURED,
|
||||
message: `You are attempting to preprocess a TypeScript file, but do not have TypeScript configured. Pass the 'typescript' option to enable TypeScript support.
|
||||
|
||||
The file: ${filePath}`,
|
||||
})
|
||||
}
|
||||
|
||||
const bundler = browserify(browserifyOptions)
|
||||
|
||||
if (file.shouldWatch) {
|
||||
debug('watching')
|
||||
bundler.plugin(watchify, watchifyOptions)
|
||||
}
|
||||
|
||||
// yield the bundle if onBundle is specified so the user can modify it
|
||||
// as need via `bundle.external()`, `bundle.plugin()`, etc
|
||||
const onBundle = options.onBundle
|
||||
|
||||
if (typeof onBundle === 'function') {
|
||||
onBundle(bundler)
|
||||
}
|
||||
|
||||
// this kicks off the bundling and wraps it up in a promise. the promise
|
||||
// is what is ultimately returned from this function
|
||||
// it resolves with the outputPath so Cypress knows where to serve
|
||||
// the file from
|
||||
const bundle = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
debug(`making bundle ${outputPath}`)
|
||||
|
||||
const onError = (err) => {
|
||||
err.filePath = filePath
|
||||
// backup the original stack before its
|
||||
// potentially modified from bluebird
|
||||
err.originalStack = err.stack
|
||||
debug(`errored bundling: ${outputPath}`, err)
|
||||
reject(err)
|
||||
}
|
||||
|
||||
const ws = fs.createWriteStream(outputPath)
|
||||
|
||||
ws.on('finish', () => {
|
||||
debug('finished bundling:', outputPath)
|
||||
resolve(outputPath)
|
||||
})
|
||||
|
||||
ws.on('error', onError)
|
||||
|
||||
bundler
|
||||
.bundle()
|
||||
.on('error', onError)
|
||||
.pipe(ws)
|
||||
})
|
||||
}
|
||||
|
||||
// when we're notified of an update via watchify, signal for Cypress to
|
||||
// rerun the spec
|
||||
bundler.on('update', () => {
|
||||
debug('update:', filePath)
|
||||
// we overwrite the cached bundle promise, so on subsequent invocations
|
||||
// it gets the latest bundle
|
||||
const bundlePromise = bundle().finally(() => {
|
||||
debug('- update finished for:', filePath)
|
||||
file.emit('rerun')
|
||||
})
|
||||
|
||||
bundles[filePath] = bundlePromise
|
||||
// we suppress unhandled rejections so they don't bubble up to the
|
||||
// unhandledRejection handler and crash the app. Cypress will eventually
|
||||
// take care of the rejection when the file is requested
|
||||
bundlePromise.suppressUnhandledRejections()
|
||||
})
|
||||
|
||||
const bundlePromise = fs
|
||||
.ensureDir(path.dirname(outputPath))
|
||||
.then(bundle)
|
||||
|
||||
// cache the bundle promise, so it can be returned if this function
|
||||
// is invoked again with the same filePath
|
||||
bundles[filePath] = bundlePromise
|
||||
|
||||
// when the spec or project is closed, we need to clean up the cached
|
||||
// bundle promise and stop the watcher via `bundler.close()`
|
||||
file.on('close', () => {
|
||||
debug('close:', filePath)
|
||||
delete bundles[filePath]
|
||||
if (file.shouldWatch) {
|
||||
bundler.close()
|
||||
}
|
||||
})
|
||||
|
||||
// return the promise, which will resolve with the outputPath or reject
|
||||
// with any error encountered
|
||||
return bundlePromise
|
||||
}
|
||||
}
|
||||
|
||||
// provide a clone of the default options
|
||||
preprocessor.defaultOptions = JSON.parse(JSON.stringify(defaultOptions))
|
||||
|
||||
preprocessor.errorTypes = errorTypes
|
||||
|
||||
if (process.env.__TESTING__) {
|
||||
preprocessor.reset = () => {
|
||||
for (let filePath in bundles) {
|
||||
delete bundles[filePath]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = preprocessor
|
Reference in New Issue
Block a user