diff --git a/.flowconfig b/.flowconfig index b6a458a1..a95afe3f 100644 --- a/.flowconfig +++ b/.flowconfig @@ -2,6 +2,7 @@ /packages/fbt/lib/.* /packages/fbt/dist/.* .*/@babel/types/lib/index.js.flow +.*/packages/babel-plugin-fbt/dist/.* [include] node_modules diff --git a/.gitignore b/.gitignore index ac1e2b31..a2790990 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ node_modules yarn-error.log .flow-results +# file checksums used by gulp-once +.checksums # MacOS .DS_Store diff --git a/CHANGELOG.md b/CHANGELOG.md index ace567dd..fbc67312 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -112,6 +112,7 @@ List of changes for each released npm package version. Unreleased changes that have landed in master. Click to see more. + - [minor] Add ability to write Flow annotations in JS code directly. Npm packages will contain both ES5 and Flow JS file versions. - [chore] Adding @noflow annotations - [fix] Fix issue where the value of the `human` option of `fbt:pronoun` was processed incorrectly. Before, `human=true` used to behave as if `human=false`, and vice versa. Also, when `fbt:pronoun` is used without an explicit `human=false` option, we'll now generate the `NOT_A_PERSON` gender-case. diff --git a/babelPlugins.js b/babelPlugins.js new file mode 100644 index 00000000..2448e81b --- /dev/null +++ b/babelPlugins.js @@ -0,0 +1,48 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @noflow + */ + +'use strict'; + +// TODO T40028530: Remove this when we can convert to fbjs +// Depends on https://github.com/facebook/fbt/issues/40 +module.exports = { + PLUGINS: [ + require('@babel/plugin-proposal-optional-catch-binding'), + require('@babel/plugin-syntax-class-properties'), + require('@babel/plugin-syntax-flow'), + require('babel-plugin-syntax-trailing-function-commas'), + require('@babel/plugin-syntax-object-rest-spread'), + require('babel-preset-fbjs/plugins/dev-expression'), + require('@babel/plugin-transform-template-literals'), + require('@babel/plugin-transform-literals'), + require('@babel/plugin-transform-function-name'), + require('@babel/plugin-transform-arrow-functions'), + require('@babel/plugin-transform-block-scoped-functions'), + require('@babel/plugin-proposal-class-properties'), + require('@babel/plugin-proposal-nullish-coalescing-operator'), + require('@babel/plugin-proposal-optional-chaining'), + [require('@babel/plugin-transform-classes'), {loose: true}], + require('@babel/plugin-transform-object-super'), + require('@babel/plugin-transform-shorthand-properties'), + require('@babel/plugin-transform-computed-properties'), + require('@babel/plugin-transform-flow-strip-types'), + require('@babel/plugin-transform-for-of'), + [require('@babel/plugin-transform-spread'), {loose: true}], + require('@babel/plugin-transform-parameters'), + [require('@babel/plugin-transform-destructuring'), {loose: true}], + require('@babel/plugin-transform-block-scoping'), + require('@babel/plugin-transform-modules-commonjs'), + require('@babel/plugin-transform-member-expression-literals'), + require('@babel/plugin-transform-property-literals'), + require('@babel/plugin-proposal-object-rest-spread'), + require('@babel/plugin-transform-react-display-name'), + require('babel-preset-fbjs/plugins/object-assign'), + ], +}; diff --git a/babelPresets.js b/babelPresets.js deleted file mode 100644 index bab5881e..00000000 --- a/babelPresets.js +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - * @noflow - */ -'use strict'; - -const moduleMap = require('./moduleMap'); - -// TODO T40028530: Remove this when we can convert to fbjs -// Depends on https://github.com/facebook/fbt/issues/40 -module.exports = function babelPresets() { - return { - plugins: [ - require('@babel/plugin-proposal-optional-catch-binding'), - require('@babel/plugin-syntax-class-properties'), - require('@babel/plugin-syntax-flow'), - require('@babel/plugin-syntax-jsx'), - require('babel-plugin-syntax-trailing-function-commas'), - require('@babel/plugin-syntax-object-rest-spread'), - require('babel-preset-fbjs/plugins/dev-expression'), - require('babel-plugin-fbt'), - require('babel-plugin-fbt-runtime'), - [require('babel-preset-fbjs/plugins/rewrite-modules'), {map: moduleMap}], - require('@babel/plugin-transform-template-literals'), - require('@babel/plugin-transform-literals'), - require('@babel/plugin-transform-function-name'), - require('@babel/plugin-transform-arrow-functions'), - require('@babel/plugin-transform-block-scoped-functions'), - require('@babel/plugin-proposal-class-properties'), - require('@babel/plugin-proposal-nullish-coalescing-operator'), - require('@babel/plugin-proposal-optional-chaining'), - [require('@babel/plugin-transform-classes'), {loose: true}], - require('@babel/plugin-transform-object-super'), - require('@babel/plugin-transform-shorthand-properties'), - require('@babel/plugin-transform-computed-properties'), - require('@babel/plugin-transform-flow-strip-types'), - require('@babel/plugin-transform-for-of'), - [require('@babel/plugin-transform-spread'), {loose: true}], - require('@babel/plugin-transform-parameters'), - [require('@babel/plugin-transform-destructuring'), {loose: true}], - require('@babel/plugin-transform-block-scoping'), - require('@babel/plugin-transform-modules-commonjs'), - require('@babel/plugin-transform-member-expression-literals'), - require('@babel/plugin-transform-property-literals'), - require('@babel/plugin-proposal-object-rest-spread'), - require('@babel/plugin-transform-react-display-name'), - require('@babel/plugin-transform-react-jsx'), - require('babel-preset-fbjs/plugins/object-assign'), - ], - }; -}; diff --git a/demo-app/package.json b/demo-app/package.json index 68616807..3bfe4a2f 100644 --- a/demo-app/package.json +++ b/demo-app/package.json @@ -13,6 +13,7 @@ "clean-fbts": "rm .enum_manifest.json .src_manifest.json .source_strings.json src/translatedFbts.json .test_*.json 2&> /dev/null || exit 0", "collect-fbts": "fbt-collect --pretty --manifest < .src_manifest.json > .source_strings.json", "manifest": "fbt-manifest --src src", + "preinstall": "yarn workspace babel-plugin-fbt prepack", "postinstall": "yarn all", "start": "webpack-dev-server --open", "test-collect-fbts": "fbt-collect --pretty --manifest < .src_manifest.json > .test_source_strings.json", diff --git a/gulpfile.js b/gulpfile.js index 350cd515..2bfad3d9 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -10,8 +10,9 @@ 'use strict'; -const babelPresets = require('./babelPresets'); +const {PLUGINS} = require('./babelPlugins'); const moduleMap = require('./moduleMap'); +const babelPluginFbtGulp = require('./packages/babel-plugin-fbt/gulpfile'); const {version} = require('./packages/fbt/package.json'); const del = require('del'); const gulp = require('gulp'); @@ -22,6 +23,7 @@ const derequire = require('gulp-derequire'); const flatten = require('gulp-flatten'); const header = require('gulp-header'); const gulpif = require('gulp-if'); +const once = require('gulp-once'); const rename = require('gulp-rename'); const rewriteModules = require('gulp-rewrite-flowtyped-modules'); const gulpUtil = require('gulp-util'); @@ -41,10 +43,6 @@ const paths = { css: ['runtime/**/*.css'], }; -const babelOptsJS = { - presets: [babelPresets()], -}; - const COPYRIGHT = 'Copyright (c) Facebook, Inc. and its affiliates.'; const COPYRIGHT_HEADER = `/** @@ -103,9 +101,28 @@ function flatLib(job) { gulp.task( 'modules', - gulp.series(() => - flatLib(gulp.src(paths.runtime, {follow: true}).pipe(babel(babelOptsJS))), - ), + gulp.series(babelPluginFbtGulp.build, function buildModules() { + return flatLib( + gulp + .src(paths.runtime, {follow: true}) + .pipe(once()) + .pipe( + babel({ + plugins: [ + ...PLUGINS, + require('@babel/plugin-syntax-jsx'), + require('babel-plugin-fbt'), + require('babel-plugin-fbt-runtime'), + [ + require('babel-preset-fbjs/plugins/rewrite-modules'), + {map: moduleMap}, + ], + require('@babel/plugin-transform-react-jsx'), + ], + }), + ), + ); + }), ); // Copy raw source with rewritten modules to *.js.flow @@ -168,13 +185,14 @@ gulp.task( gulp.task( 'clean', - gulp.series(function () { - return del([ + gulp.parallel(babelPluginFbtGulp.clean, () => + del([ + '.checksums', paths.published + '/*', '!' + paths.published + '/package.json', '!' + paths.published + '/README.md', - ]); - }), + ]), + ), ); gulp.task( diff --git a/jest-preprocessor.js b/jest-preprocessor.js index 4e8d1be5..24a17c8a 100644 --- a/jest-preprocessor.js +++ b/jest-preprocessor.js @@ -12,7 +12,17 @@ const cacheKeyPackages = [ 'babel-preset-fbjs', 'babel-plugin-fbt', 'babel-plugin-fbt-runtime', -].map(name => path.join(path.dirname(require.resolve(name)), 'package.json')); +].map(name => + path.join( + path.dirname( + // Find the actual module root from the package.json file, + // otherwise, the result may be incorrect if a custom "main" path was set. + // See https://stackoverflow.com/a/49455609/104598 + require.resolve(path.join(name, 'package.json')), + ), + 'package.json', + ), +); // This is basically fbjs-scripts/jest/preprocessor, but with the // ability to specify additional plugins @@ -25,7 +35,7 @@ function createTransformer(opts /*: Object */ = {}) { require('babel-preset-fbjs'), ], plugins: opts.plugins || [], - filename: filename, + filename, retainLines: true, }; diff --git a/jest.config.js b/jest.config.js index 0f50901c..6b8b1a58 100644 --- a/jest.config.js +++ b/jest.config.js @@ -31,6 +31,7 @@ module.exports = { { displayName: 'babel-plugin-fbt', roots: [fs.realpathSync(path.resolve('packages', 'babel-plugin-fbt'))], + testPathIgnorePatterns: ['packages/babel-plugin-fbt/dist/'], }, { displayName: 'babel-plugin-fbt-runtime', diff --git a/package.json b/package.json index 942712d8..c82de11b 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "gulp-flatten": "^0.4.0", "gulp-header": "^2.0.9", "gulp-if": "^2.0.2", + "gulp-once": "^2.1.1", "gulp-rename": "^1.4.0", "gulp-rewrite-flowtyped-modules": "^0.0.8", "gulp-util": "^3.0.8", @@ -66,6 +67,9 @@ "flow:check": "flow check --show-all-errors", "flow:watch": "yarn watch 'yarn flow stop && yarn flow check --show-all-errors 2>&1 | tee .flow-results' . --ignoreDotFiles", "postinstall": "yarn build-runtime", + "//": "Needed because the yarn+workspace `install` process creates the bin scripts' symbolic links ONLY BEFORE running the install steps", + "create_dummy_bin_scripts_for_yarn_install": "mkdir -p packages/babel-plugin-fbt/dist/bin 2> /dev/null ; cp packages/babel-plugin-fbt/bin/*.bin.js packages/babel-plugin-fbt/dist/bin", + "preinstall": "yarn create_dummy_bin_scripts_for_yarn_install", "test:watch": "jest --watch", "test": "jest" }, @@ -76,8 +80,8 @@ }, "workspaces": [ "demo-app", - "packages/babel-plugin-fbt", "packages/babel-plugin-fbt-runtime", + "packages/babel-plugin-fbt", "packages/fb-babel-plugin-utils", "packages/fb-tiger-hash", "packages/fbt" diff --git a/packages/babel-plugin-fbt/.gitignore b/packages/babel-plugin-fbt/.gitignore new file mode 100644 index 00000000..221070c9 --- /dev/null +++ b/packages/babel-plugin-fbt/.gitignore @@ -0,0 +1,6 @@ +node_modules +dist +yarn.lock + +# file checksums used by gulp-once +.checksums diff --git a/packages/babel-plugin-fbt/.npmignore b/packages/babel-plugin-fbt/.npmignore index e6d9098a..84138512 100644 --- a/packages/babel-plugin-fbt/.npmignore +++ b/packages/babel-plugin-fbt/.npmignore @@ -1,8 +1,9 @@ # Since .gitignore is not considered when .npmignore is present node_modules -translate/__tests__ -bin/__fixtures__ -bin/__tests__ -__mocks__ -__tests__ +# Example of expected npm output files: P150537602 +** +!dist/** +**/__tests__ +**/__fixtures__ +**/__mocks__ diff --git a/packages/babel-plugin-fbt/bin/__tests__/collectFBT-test.js b/packages/babel-plugin-fbt/bin/__tests__/collectFBT-test.js index c3163e77..5d49890a 100644 --- a/packages/babel-plugin-fbt/bin/__tests__/collectFBT-test.js +++ b/packages/babel-plugin-fbt/bin/__tests__/collectFBT-test.js @@ -14,8 +14,16 @@ const commonPath = path.resolve(__dirname, 'FbtCommonForTests.json'); describe('collectFBT', () => { function collect(source, options = {}) { - var collectOptions = [ - require.resolve('../collectFBT'), + const scriptPath = path.join( + // Find the actual module root path + // It's dependent on the "main" path set in package.json + // See https://stackoverflow.com/a/49455609/104598 + path.dirname(require.resolve('babel-plugin-fbt')), + 'bin', + 'collectFBT', + ); + const collectOptions = [ + scriptPath, '--packager=none', '--fbt-common-path=' + commonPath, ]; diff --git a/packages/babel-plugin-fbt/gulpfile.js b/packages/babel-plugin-fbt/gulpfile.js new file mode 100644 index 00000000..181794b9 --- /dev/null +++ b/packages/babel-plugin-fbt/gulpfile.js @@ -0,0 +1,99 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @noflow + */ + +'use strict'; + +const {PLUGINS} = require('../babelPlugins'); +const del = require('del'); +const gulp = require('gulp'); +const babel = require('gulp-babel'); +const gulpOnce = require('gulp-once'); +const rename = require('gulp-rename'); +const path = require('path'); + +const paths = { + root: ['**/*.js', '!dist/**', '!gulpfile.js', '!node_modules/**'], + dist: 'dist', +}; + +const checksumFile = '.checksums'; +const once = () => gulpOnce({file: path.join(__dirname, checksumFile)}); + +const src = (glob, opts) => + gulp.src(glob, { + cwd: __dirname, + ...opts, + }); + +const dest = (glob, opts) => + gulp.dest(glob, { + cwd: __dirname, + ...opts, + }); + +gulp.task( + 'build', + gulp.parallel( + function babelPluginFbt_buildDistJS() { + return src(paths.root, { + follow: true, + }) + .pipe(once()) + .pipe( + babel({ + plugins: PLUGINS, + }), + ) + .pipe(dest(paths.dist)); + }, + function babelPluginFbt_buildDistFlowJS() { + return src(paths.root, { + follow: true, + }) + .pipe(rename({extname: '.js.flow'})) + .pipe(once()) + .pipe(dest(paths.dist)); + }, + ), +); + +gulp.task('watch', () => { + gulp.watch( + paths.root, + { + cwd: __dirname, + ignoreInitial: false, + }, + function watchBabelPluginFbt(done) { + gulp.task('build')(done); + }, + ); +}); + +gulp.task( + 'clean', + gulp.series(() => + del( + [ + path.join(__dirname, checksumFile), + path.join(__dirname, paths.dist, '*'), + ], + {force: true}, + ), + ), +); + +gulp.task('default', gulp.series('build')); + +module.exports = { + build: gulp.task('build'), + clean: gulp.task('clean'), + watch: gulp.task('watch'), +}; diff --git a/packages/babel-plugin-fbt/package.json b/packages/babel-plugin-fbt/package.json index a3120dcd..bce78ac0 100644 --- a/packages/babel-plugin-fbt/package.json +++ b/packages/babel-plugin-fbt/package.json @@ -1,16 +1,21 @@ { "license": "MIT", - "main": "index.js", + "main": "dist/index.js", "name": "babel-plugin-fbt", "description": "The FBT Babel localization transform", "//version": "Follow SemVer specs at https://semver.org/", "version": "0.16.0", "bin": { - "fbt-collect": "bin/collectFBT.bin.js", - "fbt-manifest": "bin/manifest.bin.js", - "fbt-translate": "bin/translate.bin.js" + "fbt-collect": "dist/bin/collectFBT.bin.js", + "fbt-manifest": "dist/bin/manifest.bin.js", + "fbt-translate": "dist/bin/translate.bin.js" + }, + "devDependencies": { + "gulp": "^4.0.0", + "gulp-babel": "^8.0.0", + "gulp-once": "^2.1.1", + "gulp-rename": "^1.4.0" }, - "devDependencies": {}, "dependencies": { "@babel/core": "^7.0.0", "@babel/generator": "^7.0.0", @@ -32,6 +37,7 @@ "directory": "packages/babel-plugin-fbt" }, "scripts": { + "prepack": "gulp build", "publish_to_npm_latest": "yarn publish . && git push --tags && git push" } } diff --git a/yarn.lock b/yarn.lock index 3ec87ef7..9c86ee61 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3980,6 +3980,12 @@ gulp-match@^1.0.3: dependencies: minimatch "^3.0.3" +gulp-once@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/gulp-once/-/gulp-once-2.1.1.tgz#2aa2ead9248dd07df02fdd85e5af5dd3a96641e8" + dependencies: + plugin-error "^1.0.1" + gulp-rename@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/gulp-rename/-/gulp-rename-1.4.0.tgz#de1c718e7c4095ae861f7296ef4f3248648240bd"