Skip to content

Commit 93e20e7

Browse files
committed
feat: add typescript support
1 parent 8f3c5ee commit 93e20e7

File tree

7 files changed

+68
-22
lines changed

7 files changed

+68
-22
lines changed

README.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@ The CLI can build code for following targets:
99
- Generic CommonJS build
1010
- ES modules build for bundlers such as webpack
1111
- Flow definitions (copies .js files to .flow files)
12+
- TypeScript definitions (uses tsc to generate declaration files)
1213

1314
## Why?
1415

1516
Metro handles compiling source code for React Native libraries, but it's possible to use them in other targets such as web. Currently, to handle this, we need to have multiple babel configs and write a long `babel-cli` command in our `package.json`. We also need to keep the configs in sync between our projects.
1617

1718
Just as an example, this is a command we have in one of the packages: `babel --extensions '.js,.ts,.tsx' --no-babelrc --config-file=./babel.config.publish.js src --ignore '**/__tests__/**' --copy-files --source-maps --delete-dir-on-start --out-dir dist && del-cli 'dist/**/__tests__' && yarn tsc --emitDeclarationOnly`. This isn't all, there's even a separate `babel.config.publish.js` file. And this only works for webpack and Metro, and will fail on Node due to ESM usage.
1819

19-
Bob wraps tools such as `babel-cli` and simplifies these common tasks across multiple projects. It's tailored specifically to React Native projects to minimize the configuration required.
20+
Bob wraps tools such as `babel` and `typescript` to simplify these common tasks across multiple projects. It's tailored specifically to React Native projects to minimize the configuration required.
2021

2122
## Installation
2223

@@ -40,7 +41,8 @@ To configure your project manually, follow these steps:
4041
"output": "lib",
4142
"targets": [
4243
["commonjs", {"flow": true}],
43-
"module"
44+
"module",
45+
"typescript",
4446
]
4547
}
4648
```
@@ -59,6 +61,7 @@ To configure your project manually, follow these steps:
5961
"main": "lib/commonjs/index.js",
6062
"module": "lib/module/index.js",
6163
"react-native": "src/index.js",
64+
"typescript": "lib/typescript/src/index.d.ts",
6265
"files": [
6366
"lib/",
6467
"src/"
@@ -74,10 +77,6 @@ To configure your project manually, follow these steps:
7477

7578
And we're done 🎉
7679

77-
## TODO
78-
79-
- TypeScript support
80-
8180
## LICENSE
8281

8382
MIT

src/cli.ts

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import isGitDirty from 'is-git-dirty';
77
import * as logger from './utils/logger';
88
import buildCommonJS from './targets/commonjs';
99
import buildModule from './targets/module';
10+
import buildTypescript from './targets/typescript';
1011
import { Options } from './types';
1112

1213
const { name } = require('../package.json');
@@ -32,19 +33,31 @@ yargs
3233

3334
if (!(await fs.pathExists(pak))) {
3435
logger.exit(
35-
`Couldn't find a 'package.json' file in ${pak}. Are you in a project folder?`
36+
`Couldn't find a 'package.json' file in '${root}'. Are you in a project folder?`
37+
);
38+
}
39+
const { source } = await inquirer.prompt({
40+
type: 'input',
41+
name: 'source',
42+
message: 'Where are your source files?',
43+
default: 'src',
44+
validate: input => Boolean(input),
45+
});
46+
47+
if (
48+
!(
49+
(await fs.pathExists(path.join(root, source, 'index.js'))) ||
50+
(await fs.pathExists(path.join(root, source, 'index.ts'))) ||
51+
(await fs.pathExists(path.join(root, source, 'index.tsx')))
52+
)
53+
) {
54+
logger.exit(
55+
`Couldn't find a 'index.js'. 'index.ts' or 'index.tsx' file under '${source}'. Please re-run the CLI after creating it.`
3656
);
3757
}
3858

3959
const pkg = JSON.parse(await fs.readFile(pak, 'utf-8'));
40-
const { source, output, targets, flow } = await inquirer.prompt([
41-
{
42-
type: 'input',
43-
name: 'source',
44-
message: 'Where are your source files?',
45-
default: 'src',
46-
validate: input => Boolean(input),
47-
},
60+
const { output, targets, flow } = await inquirer.prompt([
4861
{
4962
type: 'input',
5063
name: 'output',
@@ -56,7 +69,7 @@ yargs
5669
type: 'checkbox',
5770
name: 'targets',
5871
message: 'Which targets do you want to build?',
59-
choices: ['commonjs', 'module'],
72+
choices: ['commonjs', 'module', 'typescript'],
6073
validate: input => Boolean(input.length),
6174
},
6275
{
@@ -71,6 +84,7 @@ yargs
7184
const entries = {
7285
main: path.join(output, target, 'index.js'),
7386
module: path.join(output, 'module', 'index.js'),
87+
types: path.join(output, 'typescript', source, 'index.d.ts'),
7488
'react-native': path.join(source, 'index.js'),
7589
};
7690

@@ -226,6 +240,13 @@ yargs
226240
options: targetOptions,
227241
});
228242
break;
243+
case 'typescript':
244+
await buildTypescript({
245+
root,
246+
source: source as string,
247+
output: path.resolve(root, output as string, 'typescript'),
248+
});
249+
break;
229250
default:
230251
logger.exit(`Invalid target '${target}'.`);
231252
}

src/targets/commonjs.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import del from 'del'
1+
import del from 'del';
22
import compile from '../utils/compile';
33
import * as logger from '../utils/logger';
44
import { Input } from '../types';
@@ -7,7 +7,12 @@ type Options = Input & {
77
options?: { flow?: boolean };
88
};
99

10-
export default async function build({ root, source, output, options }: Options) {
10+
export default async function build({
11+
root,
12+
source,
13+
output,
14+
options,
15+
}: Options) {
1116
logger.info('building files for commonjs target');
1217

1318
await del([output]);

src/targets/module.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import del from 'del'
1+
import del from 'del';
22
import compile from '../utils/compile';
33
import * as logger from '../utils/logger';
44
import { Input } from '../types';

src/targets/typescript.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import path from 'path';
2+
import child_process from 'child_process';
3+
import del from 'del';
4+
import * as logger from '../utils/logger';
5+
import { Input } from '../types';
6+
7+
export default async function build({ root, output }: Input) {
8+
logger.info('building files for typscript target');
9+
10+
await del([output]);
11+
12+
child_process.execFileSync(path.join(root, 'node_modules', '.bin', 'tsc'), [
13+
'--declaration',
14+
'--emitDeclarationOnly',
15+
'--outDir',
16+
output,
17+
]);
18+
}

src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ export type Input = {
44
output: string,
55
};
66

7-
export type Target = 'commonjs' | 'module';
7+
export type Target = 'commonjs' | 'module' | 'typescript';
88

99
export type Options = {
1010
source?: string,

src/utils/compile.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,27 +20,30 @@ export default async function compile({
2020
const files = glob.sync('**/*', {
2121
cwd: source,
2222
absolute: true,
23+
nodir: true,
2324
ignore: '**/__tests__/**,**/__fixtures__/**',
2425
});
2526

2627
await Promise.all(
2728
files.map(async filepath => {
2829
const outputFilename = path
2930
.join(output, path.relative(source, filepath))
30-
.replace(/\.(js|tsx?)/, '.js');
31+
.replace(/\.(js|tsx?)$/, '.js');
3132

3233
await fs.mkdirp(path.dirname(outputFilename));
3334

34-
if (!/\.(js|tsx?)/.test(filepath)) {
35+
if (!/\.(js|tsx?)$/.test(filepath)) {
3536
// Copy files which aren't source code
3637
fs.copy(filepath, outputFilename);
38+
return;
3739
}
3840

3941
const content = await fs.readFile(filepath, 'utf-8');
4042
const result = await babel.transformAsync(content, {
4143
babelrc: false,
4244
configFile: false,
4345
sourceMaps: true,
46+
filename: filepath,
4447
...options,
4548
});
4649

0 commit comments

Comments
 (0)