/
build.js
executable file
·99 lines (94 loc) · 3.6 KB
/
build.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#!/usr/bin/env node
// @flow strict
/**
* This file transpiles the src directory into dist, but also provides Flow
* typings as part of the package. See
* https://github.com/facebook/flow/issues/1996#issuecomment-228925018 for
* background on this workaround.
*
* This file is written in JS to provide a platform agnostic means of managing
* the build.
*/
const child_process = require('child_process')
const fs = require('fs').promises
const glob = require('glob')
const path = require('path')
const rimraf = require('rimraf')
const util = require('util')
/**
* Copies a file to the dist directory in its original form but as a .flow file
* to preserve its types.
*/
const copyToDistAsFlow = (source: string): Promise<mixed> => {
return fs.copyFile(source, `dist/${path.basename(source) }.flow`)
}
const replaceInFile = (
matcher: RegExp,
replacer: string,
path: string,
): Promise<mixed> => {
return fs.readFile(path, 'utf8')
.then(fileString => fileString.replace(matcher, replacer))
}
const shellCall = (exec: string, ...args: Array<string>): Promise<mixed> => {
return new Promise((resolve, reject) => {
const p = child_process.spawn(exec, args)
p.stdout.pipe(process.stdout)
p.stderr.pipe(process.stderr)
p.on('exit', code => code == 0 ? resolve() : reject(code))
})
}
Promise.resolve()
.then(() => fs.stat('dist'))
.then(() => util.promisify(rimraf)('dist'))
.catch(() => null)
.then(() => fs.mkdir('dist'))
// We expect babel to be on the PATH. If you must run without npm/yarn, use
// PATH=node_modules/.bin as a prefix to your command.
// Babel allows a directory as the source but it seems to only transform
// index.js. We need everything transformed, so explicitly invoke babel. Even
// a single invocation of babel with all of the paths simultaenously does not
// produce the desired transformations. Maybe it's a bug? But I didn't find
// anything.
.then(() => glob
.sync('src/**/*.js')
.map(p => shellCall('babel', '-d', 'dist', p))
)
/**
* This file copy was added to work around an issue with Flow strict. The cause
* is not well understood and this doesn't seem to fix all cases of using Flow's
* stirct mode. Furthermore, it breaks projects that do not use Flow strict. The
* root ./index.js.flow workaround (see below) fixes the problem. These fixes
* can coexist.
*/
.then(() => fs.copyFile('src/index.js', './index.js.flow'))
.then(() => fs.copyFile('src/index.js', './dist/index.js.flow'))
/**
* When doing import {...} from 'flow-degen', Flow looks at the root directory
* first, and finds index.js.flow. It's just a copy of our original
* src/index.js, but the paths have been renamed to look into our src directory,
* which hasn't been transformed by babel yet. Since the .flow file is
* recognizable only by Flow, Flow will follow this import chain into the src
* directory where it can pick up more types from the untransformed code. The
* runtime import chain will still follow into the dist directory as intended.
*/
.then(() => replaceInFile(/\.\//, './src/', 'index.js.flow'))
.then(() => fs.readdir('src')
.then(files => Promise.all(files
/**
* Don't do this with index.js, because we just handled it as a special
* case above.
*/
.filter(f => !f.endsWith('index.js'))
.map(f => Promise.all([
copyToDistAsFlow('src/' + f),
fs.copyFile('src/' + f, 'dist/' + f).then(() => {
return replaceInFile(/\.\//, '../src/', 'dist/' + path.basename(f))
}),
]),
)))
)
.catch(e => {
console.error('Encountered error during build:', e)
process.exit(1)
})