Skip to content

Commit

Permalink
chore: vite-dev-server refactor to enable version bumps and testing v…
Browse files Browse the repository at this point in the history
…ite core perf improvements
  • Loading branch information
JessicaSachs authored Mar 9, 2022
2 parents 7636287 + 69989d1 commit ae884fb
Show file tree
Hide file tree
Showing 21 changed files with 689 additions and 466 deletions.
2 changes: 1 addition & 1 deletion npm/design-system/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@
"tsconfig-paths-webpack-plugin": "^3.5.1",
"typed-scss-modules": "^4.1.1",
"typescript": "^4.2.3",
"vite": "2.5.0",
"vite": "2.8.4",
"webpack": "^4.44.2"
},
"peerDependencies": {
Expand Down
30 changes: 22 additions & 8 deletions npm/vite-dev-server/client/initCypressTests.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,32 @@
// This file is merged in a <script type=module> into index.html
// it will be used to load and kick start the selected spec
import specLoaders from 'cypress:spec-loaders'
import { hasSupportPath, originAutUrl } from 'cypress:config'

const specPath = window.location.pathname.replace(originAutUrl, '')
const CypressInstance = window.Cypress = parent.Cypress

const importsToLoad = []

/* Support file import logic, this should be removed once we
* are able to return relative paths from the supportFile
* Jira #UNIFY-1260
*/
const supportFile = CypressInstance.config('supportFile')
const projectRoot = CypressInstance.config('projectRoot')

const specLoader = specLoaders[specPath]
const importsToLoad = [specLoader || (() => import(/* @vite-ignore */ specPath))]
let supportRelativeToProjectRoot = supportFile.replace(projectRoot, '')

if (hasSupportPath) {
importsToLoad.unshift(() => import('cypress:support-path'))
if (CypressInstance.config('platform') === 'win32') {
supportRelativeToProjectRoot = supportFile.replace(projectRoot.replaceAll('/', '\\'))
}

const CypressInstance = window.Cypress = parent.Cypress
if (supportFile) {
// We need a slash before /cypress/supportFile.js, this happens by default
// with the current string replacement logic.
importsToLoad.push(() => import(`${supportRelativeToProjectRoot}`))
}

/* Spec file import logic */
// We need a slash before /src/my-spec.js, this does not happen by default.
importsToLoad.push(() => import(`/${CypressInstance.spec.relative}`))

if (!CypressInstance) {
throw new Error('Tests cannot run without a reference to Cypress!')
Expand Down
12 changes: 8 additions & 4 deletions npm/vite-dev-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,25 @@
"watch": "tsc -w"
},
"dependencies": {
"debug": "^4.3.2",
"get-port": "^5.1.1"
"debug": "4.3.3",
"find-up": "6.3.0",
"get-port": "5.1.1",
"local-pkg": "0.4.1",
"pathe": "0.2.0"
},
"devDependencies": {
"@cypress/react": "0.0.0-development",
"@cypress/vue": "0.0.0-development",
"@testing-library/cypress": "7.0.4",
"@vitejs/plugin-vue": "1.2.4",
"@vitejs/plugin-vue": "2.2.4",
"@vue/compiler-sfc": "3.2.31",
"cypress": "0.0.0-development",
"eslint-plugin-vue": "7.18.0",
"mocha-junit-reporter": "^2.0.0",
"mocha-multi-reporters": "^1.5.1",
"react": "17.0.2",
"vite": "2.5.0",
"vite": "2.8.4",
"vite-plugin-inspect": "0.4.3",
"vue": "3.2.31",
"vue-eslint-parser": "7.11.0"
},
Expand Down
6 changes: 6 additions & 0 deletions npm/vite-dev-server/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const configFiles = [
'vite.config.ts',
'vite.config.js',
'vite.config.mjs',
'vite.config.cjs',
]
48 changes: 23 additions & 25 deletions npm/vite-dev-server/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,33 @@
import { debug as debugFn } from 'debug'
import { createServer, InlineConfig } from 'vite'
import { resolveServerConfig, StartDevServerOptions } from './resolveServerConfig'
const debug = debugFn('cypress:vite-dev-server:vite')
import debugFn from 'debug'
import getPort from 'get-port'
import { createServer as viteCreateServer } from 'vite'
import { createConfig } from './resolveConfig'
import type { CypressViteDevServerConfig, StartDevServer } from './types'

export { StartDevServerOptions }
const debug = debugFn('cypress:vite-dev-server:index')

export async function startDevServer (startDevServerArgs: StartDevServerOptions): Promise<Cypress.ResolvedDevServerConfig> {
if (!startDevServerArgs.viteConfig) {
debug('User did not pass in any Vite dev server configuration')
startDevServerArgs.viteConfig = {}
}
export const startDevServer = async ({ options, viteConfig = {} }: StartDevServer) => {
debug('Starting Vite Server')
let server

debug('starting vite dev server')
const resolvedConfig = await resolveServerConfig(startDevServerArgs)
const port = resolvedConfig.server!.port!
try {
const config = await createConfig({ options, viteConfig })

const viteDevServer = await createServer(resolvedConfig)
server = await viteCreateServer(config)
} catch (err) {
throw new Error(err as string)
}

await viteDevServer.listen()
debug('Vite server created')
const port = await getPort({ port: 3000 })

debug('Component testing vite server started on port', port)
await server.listen(port)
debug('Successfully launched the vite server on port', port)

return { port, close: viteDevServer.close }
}

export type CypressViteDevServerConfig = Omit<InlineConfig, 'base' | 'root'> & {
/**
* Path to an index.html file that will serve as the template in
* which your components will be rendered.
*/
indexHtmlFile?: string
return {
port,
close: server.close,
}
}

export function devServer (cypressDevServerConfig: Cypress.DevServerConfig, devServerConfig?: CypressViteDevServerConfig) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,102 +1,69 @@
import { resolve, sep } from 'path'
import debugFn from 'debug'
import { readFile } from 'fs/promises'
import Debug from 'debug'
import { ModuleNode, normalizePath, Plugin, ViteDevServer } from 'vite'
import { resolve } from 'pathe'
import type { ModuleNode, Plugin, ViteDevServer } from 'vite'
import { normalizePath } from 'vite'

const debug = Debug('cypress:vite-dev-server:plugin')
const debug = debugFn('cypress:vite-dev-server:plugins:cypress')

const pluginName = 'cypress-transform-html'
const OSSepRE = new RegExp(`\\${sep}`, 'g')

function convertPathToPosix (path: string): string {
return sep === '/'
? path
: path.replace(OSSepRE, '/')
}

const INIT_FILEPATH = resolve(__dirname, '../client/initCypressTests.js')
const INIT_FILEPATH = resolve(__dirname, '../../client/initCypressTests.js')

const HMR_DEPENDENCY_LOOKUP_MAX_ITERATION = 50

interface Spec {
absolute: string
relative: string
}

function getSpecsPathsSet (specs: Spec[]) {
return new Set<string>(
specs.map((spec) => spec.absolute),
)
}

interface Spec{
absolute: string
relative: string
}

export const makeCypressPlugin = (
projectRoot: string,
supportFilePath: string | false,
devServerEvents: NodeJS.EventEmitter,
specs: Spec[],
namespace: string,
indexHtmlFile?: string,
export const Cypress = (
options,
): Plugin => {
let base = '/'

const projectRoot = options.config.projectRoot
const supportFilePath = options.config.supportFile
const devServerEvents = options.devServerEvents
const specs = options.specs
const indexHtmlFile = options.config.devServerConfig?.indexHtmlFile

let specsPathsSet = getSpecsPathsSet(specs)
let loader

devServerEvents.on('dev-server:specs:changed', (specs: Spec[]) => {
specsPathsSet = getSpecsPathsSet(specs)
})

const posixSupportFilePath = supportFilePath ? convertPathToPosix(resolve(projectRoot, supportFilePath)) : undefined
const posixIndexHtml = indexHtmlFile ? convertPathToPosix(resolve(projectRoot, indexHtmlFile)) : undefined

return {
name: pluginName,
name: 'cypress:main',
enforce: 'pre',
configResolved (config) {
base = config.base
},
async transformIndexHtml () {
const indexHtmlPath = indexHtmlFile ? resolve(projectRoot, indexHtmlFile) : resolve(__dirname, '..', 'index.html')
const indexHtmlPath = indexHtmlFile ? resolve(projectRoot, indexHtmlFile) : resolve(__dirname, '..', '..', 'index.html')

debug('resolved the indexHtmlPath as', indexHtmlPath, 'from', indexHtmlFile)
const indexHtmlContent = await readFile(indexHtmlPath, { encoding: 'utf8' })
// find </body> last index
const endOfBody = indexHtmlContent.lastIndexOf('</body>')

// insert the script in the end of the body
return `${indexHtmlContent.substring(0, endOfBody)
}<script src="${base}cypress:client-init-test" type="module"></script>${
}<script>
${loader}
</script>${
indexHtmlContent.substring(endOfBody)
}`
},
resolveId (id) {
if (id === 'cypress:config') {
return id
}

if (id === 'cypress:support-path') {
return posixSupportFilePath
}

if (id === 'cypress:spec-loaders') {
return id
}

if (id === '/cypress:client-init-test') {
return INIT_FILEPATH
}
},
load (id) {
if (id === 'cypress:spec-loaders') {
return `export default {\n${specs.map((s) => {
return `${JSON.stringify(s.relative)}:()=>import(${JSON.stringify(s.absolute)})`
}).join(',\n')}\n}`
}

if (id === 'cypress:config') {
return `
export const hasSupportPath = ${JSON.stringify(!!supportFilePath)}
export const originAutUrl = ${JSON.stringify(`/${namespace}/iframes/${normalizePath(projectRoot)}/`)}`
}
},
configureServer: async (server: ViteDevServer) => {
loader = await readFile(INIT_FILEPATH)

server.middlewares.use(`${base}index.html`, async (req, res) => {
const transformedIndexHtml = await server.transformIndexHtml(base, '')

Expand All @@ -107,7 +74,7 @@ export const originAutUrl = ${JSON.stringify(`/${namespace}/iframes/${normalizeP
debug('handleHotUpdate - file', file)

// If the user provided IndexHtml is changed, do a full-reload
if (file === posixIndexHtml) {
if (normalizePath(file) === resolve(projectRoot, indexHtmlFile)) {
server.ws.send({
type: 'full-reload',
})
Expand Down Expand Up @@ -137,7 +104,7 @@ export const originAutUrl = ${JSON.stringify(`/${namespace}/iframes/${normalizeP
devServerEvents.emit('dev-server:compile:success')

// if we update support we know we have to re-run it all
// no need to ckeck further
// no need to check further
return []
}

Expand Down
3 changes: 3 additions & 0 deletions npm/vite-dev-server/src/plugins/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './inspect'

export * from './cypress'
25 changes: 25 additions & 0 deletions npm/vite-dev-server/src/plugins/inspect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import debugFn from 'debug'
import type { PluginOption } from 'vite'

const debug = debugFn('cypress:vite-dev-server:plugins:inspect')

export const CypressInspect = async (): Promise<(() => PluginOption) | null> => {
if (!process.env.DEBUG) return null

let Inspect

try {
Inspect = (await import('vite-plugin-inspect')).default
debug('inspect was found', Inspect)
} catch (err) {
debug(`Tried to import the inspect plugin 'vite-plugin-inspect'. It's an optional peerDependency so install it if you'd like.`)
debug(err)

return null
}

return {
...Inspect(),
name: 'cypress:inspect',
}
}
55 changes: 55 additions & 0 deletions npm/vite-dev-server/src/resolveConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* The logic inside of this file is heavily reused from
* Vitest's own config resolution logic.
* You can find it here https://github.com/vitest-dev/vitest/blob/main/packages/vitest/src/node/create.ts
*/
import debugFn from 'debug'
import { importModule } from 'local-pkg'
import { relative, resolve } from 'pathe'
import { mergeConfig } from 'vite'
import { configFiles } from './constants'
import { Cypress, CypressInspect } from './plugins/index'
import type { StartDevServer } from './types'

const debug = debugFn('cypress:vite-dev-server:resolve-config')

export const createConfig = async ({ options, viteConfig: viteOverrides = {} }: StartDevServer) => {
const root = options.config.projectRoot || resolve(process.cwd())
const { default: findUp } = await importModule('find-up')
const configFile = await findUp(configFiles, { cwd: root } as { cwd: string })

// INFO logging, a lot is logged here.
// debug('all dev-server options are', options)

if (configFile) {
debug('resolved config file at', configFile, 'using root', root)
} else if (viteOverrides) {
debug('Couldn\'t find a Vite config file, however we received a custom viteConfig', viteOverrides)
} else {
debug(`
Didn\'t resolve a Vite config AND the user didn\'t pass in a custom viteConfig.
Falling back to Vite\'s defaults.`)
}

const config = {
root,
base: `/${options.config.namespace}/src/`,
configFile,
optimizeDeps: {
entries: [
...options.specs.map((s) => relative(root, s.relative)),
options.config.supportFile ?? resolve(root, options.config.supportFile),
].filter((v) => v != null),
},
plugins: [
Cypress(options),
await CypressInspect(),
],
}

const finalConfig = mergeConfig(config, viteOverrides)

debug('The resolved server config is', JSON.stringify(finalConfig, null, 2))

return finalConfig
}
Loading

3 comments on commit ae884fb

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on ae884fb Mar 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the linux x64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/10.0.0/linux-x64/10.0-release-ae884fb9107138686c0e0d899f8303e79e4759fd/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on ae884fb Mar 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the darwin x64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/10.0.0/darwin-x64/10.0-release-ae884fb9107138686c0e0d899f8303e79e4759fd/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on ae884fb Mar 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the win32 x64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/10.0.0/win32-x64/10.0-release-ae884fb9107138686c0e0d899f8303e79e4759fd/cypress.tgz

Please sign in to comment.