Skip to content

Commit

Permalink
Fix/typescript support (#214)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
adammockor committed Mar 19, 2020
1 parent d0bef65 commit ec31f78
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 126 deletions.
28 changes: 7 additions & 21 deletions packages/react-scripts/config/paths.js
Expand Up @@ -64,27 +64,21 @@ module.exports = {
componentsBuild: resolveApp('build/components'),
patternsBuild: resolveApp('build/patterns'),
appPublic: resolveApp('public'),
appIndexJs: resolveApp('src/index.js'),
appIndexJs: resolveModule(resolveApp, 'src/index'),
appPackageJson: resolveApp('package.json'),
appSrc: resolveApp('src'),
appTsConfig: resolveApp('tsconfig.json'),
appJsConfig: resolveApp('jsconfig.json'),
componentsDir: resolveApp('src/components'),
componentsJs: resolveApp('src/components/index.js'),
componentsStaticJS: resolveApp('src/components/static.js'),
patternsDir: resolveApp('src/patterns'),
patternsJs: resolveApp('src/patterns/index.js'),
styleguideIndexJs: resolveApp('src/styleguide/index.js'),
styleguideHtml: resolveApp('src/styleguide/styleguide.html'),
scriptsDir: resolveApp('src/scripts'),
yarnLockFile: resolveApp('yarn.lock'),
testsSetup: resolveModule(resolveApp, 'src/setupTests'),
proxySetup: resolveApp('src/setupProxy.js'),
appNodeModules: resolveApp('node_modules'),
publicUrlOrPath,
libDir: resolveApp('src/lib'),
icons: resolveApp('src/assets/icons'),
tokens: resolveApp('src/lib/tokens.js'),
tokens: resolveModule(resolveApp, 'src/lib/tokens'),
staticJs: resolveModule(resolveApp, 'src/scripts/index'),
};

// @remove-on-eject-begin
Expand All @@ -98,27 +92,21 @@ module.exports = {
componentsBuild: resolveApp('build/components'),
patternsBuild: resolveApp('build/patterns'),
appPublic: resolveApp('public'),
appIndexJs: resolveApp('src/index.js'),
appIndexJs: resolveModule(resolveApp, 'src/index'),
appPackageJson: resolveApp('package.json'),
appSrc: resolveApp('src'),
appTsConfig: resolveApp('tsconfig.json'),
appJsConfig: resolveApp('jsconfig.json'),
componentsDir: resolveApp('src/components'),
componentsJs: resolveApp('src/components/index.js'),
componentsStaticJs: resolveApp('src/components/static.js'),
patternsDir: resolveApp('src/patterns'),
patternsJs: resolveApp('src/patterns/index.js'),
styleguideIndexJs: resolveApp('src/styleguide/index.js'),
styleguideHtml: resolveApp('src/styleguide/styleguide.html'),
scriptsDir: resolveApp('src/scripts'),
yarnLockFile: resolveApp('yarn.lock'),
testsSetup: resolveModule(resolveApp, 'src/setupTests'),
proxySetup: resolveApp('src/setupProxy.js'),
appNodeModules: resolveApp('node_modules'),
publicUrlOrPath,
libDir: resolveApp('src/lib'),
icons: resolveApp('src/assets/icons'),
tokens: resolveApp('src/lib/tokens.js'),
tokens: resolveModule(resolveApp, 'src/lib/tokens'),
staticJs: resolveModule(resolveApp, 'src/scripts/index'),
// These properties only exist before ejecting:
ownPath: resolveOwn('.'),
ownNodeModules: resolveOwn('node_modules'), // This is empty on npm 3
Expand Down Expand Up @@ -156,10 +144,8 @@ if (
publicUrlOrPath,
componentsDir: resolveApp('src/components'),
libDir: resolveApp('src/lib'),
patternsDir: resolveApp('src/patterns'),
styleguideIndexJs: resolveApp('src/styleguide/index.js'),
styleguideHtml: resolveApp('src/styleguide/styleguide.html'),
icons: resolveApp('src/assets/icons'),
staticJs: resolveModule(resolveApp, 'src/scripts/index'),
// These properties only exist before ejecting:
ownPath: resolveOwn('.'),
ownNodeModules: resolveOwn('node_modules'),
Expand Down
197 changes: 108 additions & 89 deletions packages/react-scripts/scripts/build.js
Expand Up @@ -77,100 +77,119 @@ const spaHtmlPaths = Object.entries(spaPaths).reduce((acc, [key, value]) => {
return acc;
}, {});

const staticPath = path.join(paths.appSrc, 'scripts', 'index.js');

// Generate configuration
const config = [
configFactory('production', {
entries: {
app: path.join(paths.appSrc, 'index.js'),
...(fs.existsSync(staticPath) && { static: staticPath }),
...getEntries('lib', paths.libDir, '/*.{js,scss,css}'),
...spaEntries,
},
spaHtmlPaths,
}),
configFactory('lib', {
entries: {
...getEntries('components', paths.componentsDir, '/*.{js,scss,css}'),
...getEntries('components', paths.componentsDir, '/**/index.js'),
...getEntries('components', paths.componentsDir, '/**/*.static.js'),
},
}),
];

// We require that you explicitly set browsers and do not fall back to
// browserslist defaults.
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
checkBrowsers(paths.appPath, isInteractive)
.then(() => {
// First, read the current file sizes in build directory.
// This lets us display how much they changed later.
return measureFileSizesBeforeBuild(paths.appBuild);
})
.then(previousFileSizes => {
// Remove all content but keep the directory so that
// if you're in it, you don't end up in Trash
fs.emptyDirSync(paths.appBuild);
// Merge with the public folder
copyPublicFolder();
// Start the webpack build
return build(previousFileSizes);
})
.then(
({ stats, previousFileSizes, warnings }) => {
if (warnings.length) {
console.log(chalk.yellow('Compiled with warnings.\n'));
console.log(warnings.join('\n\n'));
console.log(
'\nSearch for the ' +
chalk.underline(chalk.yellow('keywords')) +
' to learn more about each warning.'
);
console.log(
'To ignore, add ' +
chalk.cyan('// eslint-disable-next-line') +
' to the line before.\n'
);
} else {
console.log(chalk.green('Compiled successfully.\n'));
const appConfig = configFactory('production', {
entries: {
app: paths.appIndexJs,
...(fs.existsSync(paths.staticJs) && { static: paths.staticJs }),
...getEntries('lib', paths.libDir, '/*.{js,jsx,ts,tsx,scss,css}'),
...spaEntries,
},
spaHtmlPaths,
});

const componentsConfig = configFactory('lib', {
entries: {
...getEntries(
'components',
paths.componentsDir,
'/*.{js,jsx,ts,tsx,scss,css}'
),
...getEntries(
'components',
paths.componentsDir,
'/**/index.{js,jsx,ts,tsx}'
),
...getEntries(
'components',
paths.componentsDir,
'/**/*.static.{js,jsx,ts,tsx}'
),
},
});

const run = async () => {
await runBuild(appConfig, 'app');
await runBuild(componentsConfig, 'components');
};

run();

async function runBuild(config, buildType) {
// We require that you explicitly set browsers and do not fall back to
// browserslist defaults.
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
return checkBrowsers(paths.appPath, isInteractive)
.then(() => {
// First, read the current file sizes in build directory.
// This lets us display how much they changed later.
return measureFileSizesBeforeBuild(paths.appBuild);
})
.then(previousFileSizes => {
if (buildType === 'app') {
// Remove all content but keep the directory so that
// if you're in it, you don't end up in Trash
fs.emptyDirSync(paths.appBuild);
// Merge with the public folder
copyPublicFolder();
}
// Start the webpack build
return build(previousFileSizes, config, buildType);
})
.then(
({ stats, previousFileSizes, warnings }) => {
if (warnings.length) {
console.log(chalk.yellow(`Compiled ${buildType} with warnings.\n`));
console.log(warnings.join('\n\n'));
console.log(
'\nSearch for the ' +
chalk.underline(chalk.yellow('keywords')) +
' to learn more about each warning.'
);
console.log(
'To ignore, add ' +
chalk.cyan('// eslint-disable-next-line') +
' to the line before.\n'
);
} else {
console.log(chalk.green(`Compiled ${buildType} successfully.\n`));
}

console.log('File sizes after gzip:\n');
printFileSizesAfterBuild(
stats,
previousFileSizes,
paths.appBuild,
WARN_AFTER_BUNDLE_GZIP_SIZE,
WARN_AFTER_CHUNK_GZIP_SIZE
);
console.log();
},
err => {
const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === 'true';
if (tscCompileOnError) {
console.log(
chalk.yellow(
'Compiled with the following type errors (you may want to check these before deploying your app):\n'
)
console.log('File sizes after gzip:\n');
printFileSizesAfterBuild(
stats,
previousFileSizes,
paths.appBuild,
WARN_AFTER_BUNDLE_GZIP_SIZE,
WARN_AFTER_CHUNK_GZIP_SIZE
);
printBuildError(err);
} else {
console.log(chalk.red('Failed to compile.\n'));
printBuildError(err);
process.exit(1);
console.log();
},
err => {
const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === 'true';
if (tscCompileOnError) {
console.log(
chalk.yellow(
'Compiled with the following buildType errors (you may want to check these before deploying your app):\n'
)
);
printBuildError(err);
} else {
console.log(chalk.red('Failed to compile.\n'));
printBuildError(err);
process.exit(1);
}
}
}
)
.catch(err => {
if (err && err.message) {
console.log(err.message);
}
process.exit(1);
});
)
.catch(err => {
if (err && err.message) {
console.log(err.message);
}
process.exit(1);
});
}

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

console.log('Creating an optimized production build...');
console.log(`Creating an optimized production build of ${buildType}...`);

const compiler = webpack(config);
return new Promise((resolve, reject) => {
Expand Down
22 changes: 9 additions & 13 deletions packages/react-scripts/scripts/start.js
Expand Up @@ -103,19 +103,15 @@ choosePort(HOST, DEFAULT_PORT)
{}
);

const staticPath = path.join(paths.appSrc, 'scripts', 'index.js');

const configs = [
configFactory('development', {
entries: {
app: path.join(paths.appSrc, 'index.js'),
...(fs.existsSync(staticPath) && { static: staticPath }),
...getEntries('lib', paths.libDir, '/*.{js,scss,css}'),
...spaEntries,
},
spaHtmlPaths,
}),
];
const configs = configFactory('development', {
entries: {
app: paths.appIndexJs,
...(fs.existsSync(paths.staticJs) && { static: paths.staticJs }),
...getEntries('lib', paths.libDir, '/*.{js,jsx,ts,tsx,scss,css}'),
...spaEntries,
},
spaHtmlPaths,
});

const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
const appName = require(paths.appPackageJson).name;
Expand Down
2 changes: 1 addition & 1 deletion packages/react-scripts/scripts/utils/getEntries.js
Expand Up @@ -16,7 +16,7 @@ function getEntries(type, dirPath, globRegex) {

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

Expand Down
18 changes: 16 additions & 2 deletions packages/react-scripts/scripts/utils/getSpaPaths.js
Expand Up @@ -2,9 +2,23 @@

const glob = require('glob');
const path = require('path');
const fs = require('fs');

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

const resolveEntry = entryName => {
const entryPath = path.join(paths.appSrc, entryName);
const extension = paths.moduleFileExtensions.find(extension =>
fs.existsSync(`${entryPath}.${extension}`)
);

if (extension) {
return `${entryPath}.${extension}`;
}

return `${entryPath}.js`;
};

function getSpaEntries() {
const htmlTemplates = [
...glob.sync(path.join(paths.appSrc, '*.html')),
Expand All @@ -15,9 +29,9 @@ function getSpaEntries() {
return htmlTemplates.reduce((acc, templatePath) => {
const entryName = path.basename(templatePath, '.html');

acc[entryName] = {
acc[entryName] = {
htmlTemplatePath: templatePath,
entryPath: path.join(paths.appSrc, `${entryName}.js`)
entryPath: resolveEntry(entryName),
};

return acc;
Expand Down

0 comments on commit ec31f78

Please sign in to comment.