Skip to content

Commit ec31f78

Browse files
authored
Fix/typescript support (#214)
* fix extensions in paths (and cleanup) * fix paths to allow ts and tsx extensions * get rid of webpack multicompiler mulicompiler was not compatible with TS error handling in CRA
1 parent d0bef65 commit ec31f78

File tree

5 files changed

+141
-126
lines changed

5 files changed

+141
-126
lines changed

packages/react-scripts/config/paths.js

+7-21
Original file line numberDiff line numberDiff line change
@@ -64,27 +64,21 @@ module.exports = {
6464
componentsBuild: resolveApp('build/components'),
6565
patternsBuild: resolveApp('build/patterns'),
6666
appPublic: resolveApp('public'),
67-
appIndexJs: resolveApp('src/index.js'),
67+
appIndexJs: resolveModule(resolveApp, 'src/index'),
6868
appPackageJson: resolveApp('package.json'),
6969
appSrc: resolveApp('src'),
7070
appTsConfig: resolveApp('tsconfig.json'),
7171
appJsConfig: resolveApp('jsconfig.json'),
7272
componentsDir: resolveApp('src/components'),
73-
componentsJs: resolveApp('src/components/index.js'),
74-
componentsStaticJS: resolveApp('src/components/static.js'),
75-
patternsDir: resolveApp('src/patterns'),
76-
patternsJs: resolveApp('src/patterns/index.js'),
77-
styleguideIndexJs: resolveApp('src/styleguide/index.js'),
78-
styleguideHtml: resolveApp('src/styleguide/styleguide.html'),
79-
scriptsDir: resolveApp('src/scripts'),
8073
yarnLockFile: resolveApp('yarn.lock'),
8174
testsSetup: resolveModule(resolveApp, 'src/setupTests'),
8275
proxySetup: resolveApp('src/setupProxy.js'),
8376
appNodeModules: resolveApp('node_modules'),
8477
publicUrlOrPath,
8578
libDir: resolveApp('src/lib'),
8679
icons: resolveApp('src/assets/icons'),
87-
tokens: resolveApp('src/lib/tokens.js'),
80+
tokens: resolveModule(resolveApp, 'src/lib/tokens'),
81+
staticJs: resolveModule(resolveApp, 'src/scripts/index'),
8882
};
8983

9084
// @remove-on-eject-begin
@@ -98,27 +92,21 @@ module.exports = {
9892
componentsBuild: resolveApp('build/components'),
9993
patternsBuild: resolveApp('build/patterns'),
10094
appPublic: resolveApp('public'),
101-
appIndexJs: resolveApp('src/index.js'),
95+
appIndexJs: resolveModule(resolveApp, 'src/index'),
10296
appPackageJson: resolveApp('package.json'),
10397
appSrc: resolveApp('src'),
10498
appTsConfig: resolveApp('tsconfig.json'),
10599
appJsConfig: resolveApp('jsconfig.json'),
106100
componentsDir: resolveApp('src/components'),
107-
componentsJs: resolveApp('src/components/index.js'),
108-
componentsStaticJs: resolveApp('src/components/static.js'),
109-
patternsDir: resolveApp('src/patterns'),
110-
patternsJs: resolveApp('src/patterns/index.js'),
111-
styleguideIndexJs: resolveApp('src/styleguide/index.js'),
112-
styleguideHtml: resolveApp('src/styleguide/styleguide.html'),
113-
scriptsDir: resolveApp('src/scripts'),
114101
yarnLockFile: resolveApp('yarn.lock'),
115102
testsSetup: resolveModule(resolveApp, 'src/setupTests'),
116103
proxySetup: resolveApp('src/setupProxy.js'),
117104
appNodeModules: resolveApp('node_modules'),
118105
publicUrlOrPath,
119106
libDir: resolveApp('src/lib'),
120107
icons: resolveApp('src/assets/icons'),
121-
tokens: resolveApp('src/lib/tokens.js'),
108+
tokens: resolveModule(resolveApp, 'src/lib/tokens'),
109+
staticJs: resolveModule(resolveApp, 'src/scripts/index'),
122110
// These properties only exist before ejecting:
123111
ownPath: resolveOwn('.'),
124112
ownNodeModules: resolveOwn('node_modules'), // This is empty on npm 3
@@ -156,10 +144,8 @@ if (
156144
publicUrlOrPath,
157145
componentsDir: resolveApp('src/components'),
158146
libDir: resolveApp('src/lib'),
159-
patternsDir: resolveApp('src/patterns'),
160-
styleguideIndexJs: resolveApp('src/styleguide/index.js'),
161-
styleguideHtml: resolveApp('src/styleguide/styleguide.html'),
162147
icons: resolveApp('src/assets/icons'),
148+
staticJs: resolveModule(resolveApp, 'src/scripts/index'),
163149
// These properties only exist before ejecting:
164150
ownPath: resolveOwn('.'),
165151
ownNodeModules: resolveOwn('node_modules'),

packages/react-scripts/scripts/build.js

+108-89
Original file line numberDiff line numberDiff line change
@@ -77,100 +77,119 @@ const spaHtmlPaths = Object.entries(spaPaths).reduce((acc, [key, value]) => {
7777
return acc;
7878
}, {});
7979

80-
const staticPath = path.join(paths.appSrc, 'scripts', 'index.js');
81-
82-
// Generate configuration
83-
const config = [
84-
configFactory('production', {
85-
entries: {
86-
app: path.join(paths.appSrc, 'index.js'),
87-
...(fs.existsSync(staticPath) && { static: staticPath }),
88-
...getEntries('lib', paths.libDir, '/*.{js,scss,css}'),
89-
...spaEntries,
90-
},
91-
spaHtmlPaths,
92-
}),
93-
configFactory('lib', {
94-
entries: {
95-
...getEntries('components', paths.componentsDir, '/*.{js,scss,css}'),
96-
...getEntries('components', paths.componentsDir, '/**/index.js'),
97-
...getEntries('components', paths.componentsDir, '/**/*.static.js'),
98-
},
99-
}),
100-
];
101-
102-
// We require that you explicitly set browsers and do not fall back to
103-
// browserslist defaults.
104-
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
105-
checkBrowsers(paths.appPath, isInteractive)
106-
.then(() => {
107-
// First, read the current file sizes in build directory.
108-
// This lets us display how much they changed later.
109-
return measureFileSizesBeforeBuild(paths.appBuild);
110-
})
111-
.then(previousFileSizes => {
112-
// Remove all content but keep the directory so that
113-
// if you're in it, you don't end up in Trash
114-
fs.emptyDirSync(paths.appBuild);
115-
// Merge with the public folder
116-
copyPublicFolder();
117-
// Start the webpack build
118-
return build(previousFileSizes);
119-
})
120-
.then(
121-
({ stats, previousFileSizes, warnings }) => {
122-
if (warnings.length) {
123-
console.log(chalk.yellow('Compiled with warnings.\n'));
124-
console.log(warnings.join('\n\n'));
125-
console.log(
126-
'\nSearch for the ' +
127-
chalk.underline(chalk.yellow('keywords')) +
128-
' to learn more about each warning.'
129-
);
130-
console.log(
131-
'To ignore, add ' +
132-
chalk.cyan('// eslint-disable-next-line') +
133-
' to the line before.\n'
134-
);
135-
} else {
136-
console.log(chalk.green('Compiled successfully.\n'));
80+
const appConfig = configFactory('production', {
81+
entries: {
82+
app: paths.appIndexJs,
83+
...(fs.existsSync(paths.staticJs) && { static: paths.staticJs }),
84+
...getEntries('lib', paths.libDir, '/*.{js,jsx,ts,tsx,scss,css}'),
85+
...spaEntries,
86+
},
87+
spaHtmlPaths,
88+
});
89+
90+
const componentsConfig = configFactory('lib', {
91+
entries: {
92+
...getEntries(
93+
'components',
94+
paths.componentsDir,
95+
'/*.{js,jsx,ts,tsx,scss,css}'
96+
),
97+
...getEntries(
98+
'components',
99+
paths.componentsDir,
100+
'/**/index.{js,jsx,ts,tsx}'
101+
),
102+
...getEntries(
103+
'components',
104+
paths.componentsDir,
105+
'/**/*.static.{js,jsx,ts,tsx}'
106+
),
107+
},
108+
});
109+
110+
const run = async () => {
111+
await runBuild(appConfig, 'app');
112+
await runBuild(componentsConfig, 'components');
113+
};
114+
115+
run();
116+
117+
async function runBuild(config, buildType) {
118+
// We require that you explicitly set browsers and do not fall back to
119+
// browserslist defaults.
120+
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
121+
return checkBrowsers(paths.appPath, isInteractive)
122+
.then(() => {
123+
// First, read the current file sizes in build directory.
124+
// This lets us display how much they changed later.
125+
return measureFileSizesBeforeBuild(paths.appBuild);
126+
})
127+
.then(previousFileSizes => {
128+
if (buildType === 'app') {
129+
// Remove all content but keep the directory so that
130+
// if you're in it, you don't end up in Trash
131+
fs.emptyDirSync(paths.appBuild);
132+
// Merge with the public folder
133+
copyPublicFolder();
137134
}
135+
// Start the webpack build
136+
return build(previousFileSizes, config, buildType);
137+
})
138+
.then(
139+
({ stats, previousFileSizes, warnings }) => {
140+
if (warnings.length) {
141+
console.log(chalk.yellow(`Compiled ${buildType} with warnings.\n`));
142+
console.log(warnings.join('\n\n'));
143+
console.log(
144+
'\nSearch for the ' +
145+
chalk.underline(chalk.yellow('keywords')) +
146+
' to learn more about each warning.'
147+
);
148+
console.log(
149+
'To ignore, add ' +
150+
chalk.cyan('// eslint-disable-next-line') +
151+
' to the line before.\n'
152+
);
153+
} else {
154+
console.log(chalk.green(`Compiled ${buildType} successfully.\n`));
155+
}
138156

139-
console.log('File sizes after gzip:\n');
140-
printFileSizesAfterBuild(
141-
stats,
142-
previousFileSizes,
143-
paths.appBuild,
144-
WARN_AFTER_BUNDLE_GZIP_SIZE,
145-
WARN_AFTER_CHUNK_GZIP_SIZE
146-
);
147-
console.log();
148-
},
149-
err => {
150-
const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === 'true';
151-
if (tscCompileOnError) {
152-
console.log(
153-
chalk.yellow(
154-
'Compiled with the following type errors (you may want to check these before deploying your app):\n'
155-
)
157+
console.log('File sizes after gzip:\n');
158+
printFileSizesAfterBuild(
159+
stats,
160+
previousFileSizes,
161+
paths.appBuild,
162+
WARN_AFTER_BUNDLE_GZIP_SIZE,
163+
WARN_AFTER_CHUNK_GZIP_SIZE
156164
);
157-
printBuildError(err);
158-
} else {
159-
console.log(chalk.red('Failed to compile.\n'));
160-
printBuildError(err);
161-
process.exit(1);
165+
console.log();
166+
},
167+
err => {
168+
const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === 'true';
169+
if (tscCompileOnError) {
170+
console.log(
171+
chalk.yellow(
172+
'Compiled with the following buildType errors (you may want to check these before deploying your app):\n'
173+
)
174+
);
175+
printBuildError(err);
176+
} else {
177+
console.log(chalk.red('Failed to compile.\n'));
178+
printBuildError(err);
179+
process.exit(1);
180+
}
162181
}
163-
}
164-
)
165-
.catch(err => {
166-
if (err && err.message) {
167-
console.log(err.message);
168-
}
169-
process.exit(1);
170-
});
182+
)
183+
.catch(err => {
184+
if (err && err.message) {
185+
console.log(err.message);
186+
}
187+
process.exit(1);
188+
});
189+
}
171190

172191
// Create the production build and print the deployment instructions.
173-
function build(previousFileSizes) {
192+
function build(previousFileSizes, config, buildType) {
174193
// We used to support resolving modules according to `NODE_PATH`.
175194
// This now has been deprecated in favor of jsconfig/tsconfig.json
176195
// This lets you use absolute paths in imports inside large monorepos:
@@ -183,7 +202,7 @@ function build(previousFileSizes) {
183202
console.log();
184203
}
185204

186-
console.log('Creating an optimized production build...');
205+
console.log(`Creating an optimized production build of ${buildType}...`);
187206

188207
const compiler = webpack(config);
189208
return new Promise((resolve, reject) => {

packages/react-scripts/scripts/start.js

+9-13
Original file line numberDiff line numberDiff line change
@@ -103,19 +103,15 @@ choosePort(HOST, DEFAULT_PORT)
103103
{}
104104
);
105105

106-
const staticPath = path.join(paths.appSrc, 'scripts', 'index.js');
107-
108-
const configs = [
109-
configFactory('development', {
110-
entries: {
111-
app: path.join(paths.appSrc, 'index.js'),
112-
...(fs.existsSync(staticPath) && { static: staticPath }),
113-
...getEntries('lib', paths.libDir, '/*.{js,scss,css}'),
114-
...spaEntries,
115-
},
116-
spaHtmlPaths,
117-
}),
118-
];
106+
const configs = configFactory('development', {
107+
entries: {
108+
app: paths.appIndexJs,
109+
...(fs.existsSync(paths.staticJs) && { static: paths.staticJs }),
110+
...getEntries('lib', paths.libDir, '/*.{js,jsx,ts,tsx,scss,css}'),
111+
...spaEntries,
112+
},
113+
spaHtmlPaths,
114+
});
119115

120116
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
121117
const appName = require(paths.appPackageJson).name;

packages/react-scripts/scripts/utils/getEntries.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ function getEntries(type, dirPath, globRegex) {
1616

1717
let entryName = path
1818
// get rid of extension from entry name
19-
.join(type, localPath.split(/(\.js|\.css|\.scss)$/)[0])
19+
.join(type, localPath.split(/(\.js|\.jsx|\.ts|\.tsx|\.css|\.scss)$/)[0])
2020
// remove leading slash
2121
.replace(/^\/|\/$/g, '');
2222

packages/react-scripts/scripts/utils/getSpaPaths.js

+16-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,23 @@
22

33
const glob = require('glob');
44
const path = require('path');
5+
const fs = require('fs');
56

67
const paths = require('../../config/paths');
78

9+
const resolveEntry = entryName => {
10+
const entryPath = path.join(paths.appSrc, entryName);
11+
const extension = paths.moduleFileExtensions.find(extension =>
12+
fs.existsSync(`${entryPath}.${extension}`)
13+
);
14+
15+
if (extension) {
16+
return `${entryPath}.${extension}`;
17+
}
18+
19+
return `${entryPath}.js`;
20+
};
21+
822
function getSpaEntries() {
923
const htmlTemplates = [
1024
...glob.sync(path.join(paths.appSrc, '*.html')),
@@ -15,9 +29,9 @@ function getSpaEntries() {
1529
return htmlTemplates.reduce((acc, templatePath) => {
1630
const entryName = path.basename(templatePath, '.html');
1731

18-
acc[entryName] = {
32+
acc[entryName] = {
1933
htmlTemplatePath: templatePath,
20-
entryPath: path.join(paths.appSrc, `${entryName}.js`)
34+
entryPath: resolveEntry(entryName),
2135
};
2236

2337
return acc;

0 commit comments

Comments
 (0)