| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407 |
- const fs = require('fs')
- const path = require('path')
- const globby = require('globby')
- const handlebars = require('handlebars')
- const { src, dest, parallel, series } = require('gulp')
- const { readFile, writeFile, watch } = require('./scripts/lib/util')
- const replace = require('gulp-replace')
- const exec = require('./scripts/lib/shell').sync.withOptions({ // always SYNC!
- live: true,
- exitOnError: true
- // TODO: flag for echoing command?
- })
- const concurrently = require('concurrently')
- const { minifyBundleJs, minifyBundleCss } = require('./scripts/lib/minify')
- const modify = require('gulp-modify-file')
- const { allStructs, publicPackageStructs } = require('./scripts/lib/package-index')
- const semver = require('semver')
- const { eslintAll } = require('./scripts/eslint-dir')
- exports.archive = require('./scripts/lib/archive')
- /*
- copy over the vdom files that were externalized by rollup.
- we externalize these for two reasons:
- - when a consumer build system sees `import './vdom'` it's more likely to treat it with side effects.
- - rollup-plugin-dts was choking on the namespace declarations in the tsc-generated vdom.d.ts files.
- */
- const VDOM_FILE_MAP = {
- 'packages/core-preact/tsc/vdom.d.ts': 'packages/core',
- 'packages/common/tsc/vdom.d.ts': 'packages/common'
- }
- const copyVDomMisc = exports.copyVDomMisc = parallelMap(
- VDOM_FILE_MAP,
- (srcGlob, destDir) => src(srcGlob)
- .pipe(replace(/\/\/.*/g, '')) // remove sourcemap comments and ///<reference> don in rollup too
- .pipe(dest(destDir))
- )
- function parallelMap(map, execute) {
- return parallel.apply(null, Object.keys(map).map((key) => {
- let task = () => execute(key, map[key])
- task.displayName = key
- return task
- }))
- }
- const localesDts = exports.localesDts = parallel(localesAllDts, localesEachDts)
- function localesAllDts() { // needs tsc
- return src('packages/core/tsc/locales-all.d.ts')
- .pipe(removeSimpleComments())
- .pipe(dest('packages/core'))
- }
- function localesEachDts() { // needs tsc
- return src('packages/core/tsc/locales/*.d.ts')
- .pipe(removeSimpleComments())
- .pipe(dest('packages/core/locales')) // TODO: remove sourcemap comment
- }
- function removeSimpleComments() { // like a gulp plugin
- return modify(function(code) { // TODO: use gulp-replace instead
- return code.replace(/\/\/.*/g, '') // TODO: make a general util for this
- })
- }
- exports.build = series(
- series(removeTscDevLinks, writeTscDevLinks), // for tsc
- localesAllSrc, // before tsc
- execTask('tsc -b --verbose'),
- localesDts,
- removeTscDevLinks,
- execTask('webpack --config webpack.bundles.js --env NO_SOURCE_MAPS'), // always compile from SRC
- execTask('rollup -c rollup.locales.js'),
- execTask('rollup -c rollup.bundles.js'),
- execTask('rollup -c rollup.packages.js'),
- copyVDomMisc,
- minifyBundleJs,
- minifyBundleCss
- )
- exports.watch = series(
- series(removeTscDevLinks, writeTscDevLinks), // for tsc
- localesAllSrc, // before tsc
- execTask('tsc -b --verbose'), // initial run
- localesDts, // won't watch :(
- parallel(
- localesAllSrcWatch,
- execParallel({
- tsc: 'tsc -b --watch --preserveWatchOutput --pretty', // wont do pretty bc of piping
- bundles: 'webpack --config webpack.bundles.js --watch',
- locales: 'rollup -c rollup.locales.js --watch' // operates on src files. fyi: tests will need this instantly, if compiled together
- })
- )
- )
- exports.testsIndex = testsIndex
- exports.test = series(
- testsIndex,
- parallel(
- testsIndexWatch,
- execParallel({
- webpack: 'webpack --config webpack.tests.js --watch --env PACKAGES_FROM_SOURCE',
- karma: 'karma start karma.config.js'
- })
- )
- )
- exports.testCi = series(
- testsIndex,
- execTask('webpack --config webpack.tests.js'),
- execTask('karma start karma.config.js ci')
- )
- const LOCALES_SRC_DIR = 'packages/core/src/locales'
- const LOCALES_ALL_TPL = 'packages/core/src/locales-all.ts.tpl'
- const LOCALES_ALL_DEST = 'packages/core/src/locales-all.ts'
- exports.localesAllSrc = localesAllSrc
- exports.localesAllSrcWatch = localesAllSrcWatch
- async function localesAllSrc() {
- let localeFileNames = await globby('*.ts', { cwd: LOCALES_SRC_DIR })
- let localeCodes = localeFileNames.map((fileName) => path.basename(fileName, '.ts'))
- let localeImportPaths = localeCodes.map((localeCode) => `./locales/${localeCode}`)
- let templateText = await readFile(LOCALES_ALL_TPL)
- let template = handlebars.compile(templateText)
- let jsText = template({
- localeImportPaths
- })
- await writeFile(LOCALES_ALL_DEST, jsText)
- }
- function localesAllSrcWatch() {
- return watch([ LOCALES_SRC_DIR, LOCALES_ALL_TPL ], localesAllSrc)
- }
- exports.writeTscDevLinks = series(removeTscDevLinks, writeTscDevLinks)
- exports.removeTscDevLinks = removeTscDevLinks
- async function writeTscDevLinks() { // bad name. does js AND .d.ts. is it necessary to do the js?
- for (let struct of publicPackageStructs) {
- let jsOut = path.join(struct.dir, struct.mainDistJs)
- let dtsOut = path.join(struct.dir, struct.mainDistDts)
- exec([
- 'mkdir',
- '-p',
- path.dirname(jsOut),
- path.dirname(dtsOut),
- ])
- exec([ 'ln', '-s', struct.mainTscJs, jsOut ])
- exec([ 'ln', '-s', struct.mainTscDts, dtsOut ])
- }
- }
- async function removeTscDevLinks() {
- for (let struct of publicPackageStructs) {
- let jsLink = path.join(struct.dir, struct.mainDistJs)
- let dtsLink = path.join(struct.dir, struct.mainDistDts)
- exec([ 'rm', '-f', jsLink, dtsLink ])
- }
- }
- const exec2 = require('./scripts/lib/shell').sync
- exports.testsIndex = testsIndex
- exports.testsIndexWatch = testsIndexWatch
- async function testsIndex() {
- let res = exec2(
- "find packages*/__tests__/src -mindepth 2 -type f \\( -name '*.ts' -or -name '*.tsx' \\) -print0 | " +
- 'xargs -0 grep -E "(fdescribe|fit)\\("'
- )
- if (!res.success && res.stderr) { // means there was a real error
- throw new Error(res.stderr)
- }
- let files
- if (!res.success) { // means there were no files that matched
- let { stdout } = exec2("find packages*/__tests__/src -mindepth 2 -type f \\( -name '*.ts' -or -name '*.tsx' \\)")
- files = stdout.trim()
- files = !files ? [] : files.split('\n')
- files = uniqStrs(files)
- files.sort() // work around OS-dependent sorting ... TODO: better sorting that knows about filename slashes
- console.log(`[test-index] All ${files.length} test files.`) // TODO: use gulp log util?
- } else {
- files = res.stdout.trim()
- files = !files ? [] : files.split('\n')
- files = files.map((line) => line.trim().split(':')[0]) // TODO: do a max split of 1
- files = uniqStrs(files)
- files.sort() // work around OS-dependent sorting
- console.log(
- '[test-index] Only test files that have fdescribe/fit:\n' + // TODO: use gulp log util?
- files.map((file) => ` - ${file}`).join('\n')
- )
- }
- let mainFiles = globby.sync('packages*/__tests__/src/main.{js,ts}')
- files = mainFiles.concat(files)
- // need 'contrib:ci' to have already been run
- if (process.env.FULLCALENDAR_FORCE_REACT) {
- files = [ 'packages-contrib/react/dist/vdom-test-react18.js' ].concat(files)
- }
- let code =
- files.map(
- (file) => `import ${JSON.stringify('../../' + file)}`
- ).join('\n') +
- '\n'
- await writeFile('tmp/tests/index.js', code)
- }
- function testsIndexWatch() {
- return watch(
- [ 'packages/__tests__/src', 'packages-premium/__tests__/src' ], // wtf won't globs work for this?
- exports.testsIndex
- )
- }
- /*
- TODO: make unnecessary. have grep do this instead with the -l option:
- https://stackoverflow.com/questions/6637882/how-can-i-use-grep-to-show-just-filenames-on-linux
- */
- function uniqStrs(a) {
- let hash = {}
- for (let item of a) {
- hash[item] = true
- }
- return Object.keys(hash)
- }
- function execTask(args) {
- const exec = require('./scripts/lib/shell').promise.withOptions({ live: true })
- let name = Array.isArray(args) ? args[0] : args.match(/\w+/)[0]
- let taskFunc = () => exec(args)
- taskFunc.displayName = name
- return taskFunc
- }
- function execParallel(map) {
- let taskArray = []
- for (let taskName in map) {
- taskArray.push({ name: taskName, command: map[taskName] })
- }
- let func = () => concurrently(taskArray, { killOthers: ['failure'] })
- func.displayName = 'concurrently'
- return func
- }
- const exec3 = require('./scripts/lib/shell').sync.withOptions({
- live: true,
- exitOnError: false
- })
- exports.lintBuiltCss = function() {
- let anyFailures = false
- for (let struct of publicPackageStructs) {
- let builtCssFile = path.join(struct.dir, 'main.css')
- if (fs.existsSync(builtCssFile)) {
- let cmd = [
- 'stylelint', '--config', 'stylelint.config.js',
- builtCssFile
- ]
- console.log('Running stylelint on', struct.name, '...')
- console.log(cmd.join(' '))
- console.log()
- let { success } = exec3(cmd)
- if (!success) {
- anyFailures = true
- }
- }
- }
- if (anyFailures) {
- return Promise.reject(new Error('At least one linting job failed'))
- }
- return Promise.resolve()
- }
- exports.lintBuiltDts = function() {
- let anyFailures = false
- for (let struct of publicPackageStructs) {
- let dtsFile = path.join(struct.dir, 'main.d.ts')
- console.log(`Checking ${dtsFile}`)
- // look for bad module declarations (when relative, assumed NOT to be ambient, so BAD)
- // look for references to react/preact (should always use vdom instead)
- let { stdout } = require('./scripts/lib/shell').sync([
- 'grep', '-iEe', '(declare module [\'"]\\.|p?react)', dtsFile
- ])
- stdout = stdout.trim()
- if (stdout) { // don't worry about failure. grep gives failure if no results
- console.log(' BAD: ' + stdout)
- anyFailures = true
- }
- if (struct.isPremium && struct.name !== '@fullcalendar/premium-common') {
- let { stdout: stdout2 } = require('./scripts/lib/shell').sync([
- 'grep', '-e', '@fullcalendar/premium-common', dtsFile
- ])
- stdout2 = stdout2.trim()
- if (!stdout2) {
- console.warn(`The premium package ${struct.name} does not have @fullcalendar/premium-common reference in .d.ts`)
- anyFailures = true
- }
- }
- console.log()
- }
- if (anyFailures) {
- return Promise.reject(new Error('At least one dts linting job failed'))
- }
- return Promise.resolve()
- }
- const REQUIRED_TSLIB_SEMVER = '2'
- exports.lintPackageMeta = function() {
- let success = true
- for (let struct of publicPackageStructs) {
- let { meta } = struct
- if (!meta.main) {
- console.warn(`${struct.name} should have a 'main' entry`)
- success = false
- }
- if (!meta.module) {
- console.warn(`${struct.name} should have a 'module' entry`)
- success = false
- }
- if (meta.dependencies && meta.dependencies['@fullcalendar/core']) {
- console.warn(`${struct.name} should have @fullcalendar/common as a dep, NOT @fullcalendar/core`)
- success = false
- }
- let tslibSemver = (meta.dependencies || {}).tslib || ''
- if (!tslibSemver || !semver.intersects(tslibSemver, REQUIRED_TSLIB_SEMVER)) {
- console.warn(`${struct.name} has a tslib version ('${tslibSemver}') that does not satisfy '${REQUIRED_TSLIB_SEMVER}'`)
- success = false
- }
- if (!fs.existsSync(path.join(struct.dir, '.npmignore'))) {
- console.warn(`${struct.name} needs a .npmignore file`)
- success = false
- }
- }
- if (success) {
- return Promise.resolve()
- } else {
- return Promise.reject(new Error('At least one package.json has an error'))
- }
- }
- exports.lint = series(exports.lintPackageMeta, () => {
- return eslintAll() ? Promise.resolve() : Promise.reject(new Error('One or more lint tasks failed'))
- })
- exports.lintBuilt = series(exports.lintBuiltCss, exports.lintBuiltDts)
|