diff --git a/packages/ram-composition-root/package.json b/packages/ram-composition-root/package.json index 83b115b..125665a 100644 --- a/packages/ram-composition-root/package.json +++ b/packages/ram-composition-root/package.json @@ -18,6 +18,7 @@ "build": "cpr ./src ./dist -d" }, "dependencies": { + "chokidar": "^3.4.2", "dot-object": "^1.7.1", "lodash": "^4.17.15", "minimist": "^1.2.5" diff --git a/packages/ram-composition-root/src/bin/build-usage.txt b/packages/ram-composition-root/src/bin/build-usage.txt new file mode 100644 index 0000000..b0b3377 --- /dev/null +++ b/packages/ram-composition-root/src/bin/build-usage.txt @@ -0,0 +1,9 @@ +Usage: composition-root build [options] + + Generates composition root files in a folder for a from composition configuration file. + +Options: + + -c , --config composition config file (default: "composition-config.json"). + -o , --output output file + -h, --help Display this usage info. diff --git a/packages/ram-composition-root/src/bin/build.js b/packages/ram-composition-root/src/bin/build.js new file mode 100644 index 0000000..eec5c77 --- /dev/null +++ b/packages/ram-composition-root/src/bin/build.js @@ -0,0 +1,39 @@ +const fs = require('fs'); +const path = require('path'); + +const {loadConfig} = require('../config-loader'); +const {resolveContext} = require('../resolver'); + +module.exports = {build}; + +function build(searchRoot, referencedRule, configFile = 'composition-config.json', outputFile) { + const searchPath = path.resolve(process.cwd(), searchRoot); + const configAbsolutePath = path.resolve(searchPath, configFile); + + console.log('Loading config', require.resolve(configAbsolutePath)); + const compositionConfig = loadConfig(configAbsolutePath); + + const {plugins, rules, compositionRoots} = compositionConfig; + + const context = { + referencedRule, + cwd: searchPath, + basePath: path.relative(process.cwd(), searchRoot), + configAbsolutePath, + rootPath: '.', + plugins, + rules, + compositionRoots, + }; + + const output = resolveContext(context); + + if (!output) { + console.warn(`No files matched "${referencedRule}" rule in ${searchPath}`); + } + + if (outputFile) { + fs.writeFileSync(path.resolve(searchPath, outputFile), output); + } +} + diff --git a/packages/ram-composition-root/src/bin/cli-usage.txt b/packages/ram-composition-root/src/bin/cli-usage.txt new file mode 100644 index 0000000..ba865aa --- /dev/null +++ b/packages/ram-composition-root/src/bin/cli-usage.txt @@ -0,0 +1,11 @@ +Usage: composition-root [options] + +Commands: + + build Build composition root + watch Watch for new files + +Options: + + -h, --help Display this usage info. + -v, --version Display the composition-root version. diff --git a/packages/ram-composition-root/src/bin/cli.js b/packages/ram-composition-root/src/bin/cli.js index a37c367..31d8f5f 100644 --- a/packages/ram-composition-root/src/bin/cli.js +++ b/packages/ram-composition-root/src/bin/cli.js @@ -1,10 +1,8 @@ -const _ = require('lodash/fp'); const fs = require('fs'); -const path = require('path'); const minimist = require('minimist'); -const {loadConfig} = require('../config-loader'); -const {resolveContext} = require('../resolver'); +const {build} = require('./build'); +const {watch} = require('./watch'); const argv = minimist(process.argv.slice(2), { alias: { @@ -20,42 +18,28 @@ if (argv.v) { return; } -if (argv.h || !argv._[0]) { - fs.createReadStream(__dirname + '/usage.txt').pipe(process.stdout); - return; -} - -const searchRoot = argv._[1]; -const referencedRule = argv._[2]; - -const searchPath = path.resolve(process.cwd(), searchRoot); -const configFile = argv.c || 'composition-config.json'; -const configAbsolutePath = path.resolve(searchPath, configFile); - -console.log('Loading config', require.resolve(configAbsolutePath)); -const compositionConfig = loadConfig(configAbsolutePath); - - -const {plugins, rules, compositionRoots} = compositionConfig; - -const context = { - referencedRule, - cwd: searchPath, - basePath: path.relative(process.cwd(), searchRoot), - configAbsolutePath, - rootPath: '.', - plugins, - rules, - compositionRoots, -} - -const output = resolveContext(context); - -if (!output) { - console.warn(`No files matched "${referencedRule}" rule in ${searchPath}`); +if (argv.h) { + switch (argv._[0]) { + case 'build': + fs.createReadStream(__dirname + '/build-usage.txt').pipe(process.stdout); + return; + case 'watch': + fs.createReadStream(__dirname + '/watch-usage.txt').pipe(process.stdout); + return; + default: + fs.createReadStream(__dirname + '/cli-usage.txt').pipe(process.stdout); + return; + } } -let outputFile = argv.o; -if (outputFile) { - fs.writeFileSync(path.resolve(searchPath, outputFile), output); +switch (argv._[0]) { + case 'build': + build(argv._[1], argv._[2], argv.c, argv.o); + return; + case 'watch': + watch(argv._[1], argv._[2]); + return; + default: + fs.createReadStream(__dirname + '/cli-usage.txt').pipe(process.stdout); + return; } diff --git a/packages/ram-composition-root/src/bin/usage.txt b/packages/ram-composition-root/src/bin/usage.txt deleted file mode 100644 index 93a8e31..0000000 --- a/packages/ram-composition-root/src/bin/usage.txt +++ /dev/null @@ -1,11 +0,0 @@ -Usage: composition-root build [options] - - Generates composition root files in a "search-root" folder for a "rule" from composition configuration file. - -Options: - - -c , --config composition config file (default: "composition-config.json"). - -o , --output output file - -h, --help Display this usage info. - -v, --version Display the cpr version. - diff --git a/packages/ram-composition-root/src/bin/watch-usage.txt b/packages/ram-composition-root/src/bin/watch-usage.txt new file mode 100644 index 0000000..b2eb19d --- /dev/null +++ b/packages/ram-composition-root/src/bin/watch-usage.txt @@ -0,0 +1,5 @@ +Usage: composition-root watch [options] + + Watches and updates target timestamps whenever source files are added or deleted. This forces Babel to rerun macros within target . + + -h, --help Display this usage info. diff --git a/packages/ram-composition-root/src/bin/watch.js b/packages/ram-composition-root/src/bin/watch.js new file mode 100644 index 0000000..8af2860 --- /dev/null +++ b/packages/ram-composition-root/src/bin/watch.js @@ -0,0 +1,38 @@ +const fs = require('fs'); +const path = require('path'); +const chokidar = require('chokidar'); + +module.exports = {watch}; + +function watch(cwd, filename) { + const fileToUpdate = path.join(cwd, filename); + + console.log(`Watching ${cwd} for new files.`); + + const watcher = chokidar.watch([ + path.join(cwd, '**/*.tsx'), + path.join(cwd, '**/*.ts'), + path.join(cwd, '**/*.js'), + path.join(cwd, '**/*.jsx'), + ], { + ignored: fileToUpdate, + persistent: true, + }); + + watcher.on('ready', () => { + watcher + .on('add', path => { + console.log(`File ${path} has been added`); + return invalidateCache(fileToUpdate); + }) + .on('unlink', path => { + console.log(`File ${path} has been removed`); + return invalidateCache(fileToUpdate); + }); + }); +} + +function invalidateCache(fileName) { + console.log(`Updating ${fileName}`) + fs.utimesSync(fileName, new Date(), new Date()); +} diff --git a/packages/ram-sample-app/package.json b/packages/ram-sample-app/package.json index 0a689a3..08b88c6 100644 --- a/packages/ram-sample-app/package.json +++ b/packages/ram-sample-app/package.json @@ -10,7 +10,9 @@ "scripts": { "generate-composition": "composition-root build ./src Module", "prestart": "npm run generate-composition", - "start": "react-scripts start", + "watch": "composition-root watch ./src index.tsx", + "serve": "react-scripts start", + "start": "npm-run-all -p watch serve", "prebuild": "npm run generate-composition", "build": "react-scripts build", "test": "react-scripts test --env=jsdom", @@ -22,6 +24,7 @@ "@types/jest": "^25.2.3", "@types/lodash": "^4.14.152", "@types/node": "^14.0.5", + "npm-run-all": "^4.1.5", "patch-package": "^6.2.2", "postinstall-postinstall": "^2.1.0", "react-scripts": "^3.4.1", diff --git a/yarn.lock b/yarn.lock index abd06f5..7a5b04b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3804,6 +3804,21 @@ chokidar@^3.3.0, chokidar@^3.4.0: optionalDependencies: fsevents "~2.1.2" +chokidar@^3.4.2: + version "3.4.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.2.tgz#38dc8e658dec3809741eb3ef7bb0a47fe424232d" + integrity sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.4.0" + optionalDependencies: + fsevents "~2.1.2" + chownr@^1.1.1, chownr@^1.1.2: version "1.1.4" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" @@ -8340,6 +8355,11 @@ memory-fs@^0.5.0: errno "^0.1.3" readable-stream "^2.0.1" +memorystream@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" + integrity sha1-htcJCzDORV1j+64S3aUaR93K+bI= + meow@^3.3.0: version "3.7.0" resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" @@ -8980,6 +9000,21 @@ npm-pick-manifest@^3.0.0: npm-package-arg "^6.0.0" semver "^5.4.1" +npm-run-all@^4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.5.tgz#04476202a15ee0e2e214080861bff12a51d98fba" + integrity sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ== + dependencies: + ansi-styles "^3.2.1" + chalk "^2.4.1" + cross-spawn "^6.0.5" + memorystream "^0.3.1" + minimatch "^3.0.4" + pidtree "^0.3.0" + read-pkg "^3.0.0" + shell-quote "^1.6.1" + string.prototype.padend "^3.0.0" + npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" @@ -9617,6 +9652,11 @@ picomatch@^2.0.4, picomatch@^2.2.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== +pidtree@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.3.1.tgz#ef09ac2cc0533df1f3250ccf2c4d366b0d12114a" + integrity sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA== + pify@^2.0.0, pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -11618,7 +11658,7 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -shell-quote@1.7.2: +shell-quote@1.7.2, shell-quote@^1.6.1: version "1.7.2" resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== @@ -12045,6 +12085,14 @@ string.prototype.matchall@^4.0.2: regexp.prototype.flags "^1.3.0" side-channel "^1.0.2" +string.prototype.padend@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.1.0.tgz#dc08f57a8010dc5c153550318f67e13adbb72ac3" + integrity sha512-3aIv8Ffdp8EZj8iLwREGpQaUZiPyrWrpzMBHvkiSW/bK/EGve9np07Vwy7IJ5waydpGXzQZu/F8Oze2/IWkBaA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + string.prototype.trimend@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913"