diff --git a/README.md b/README.md index 6eb5463..db7bce4 100644 --- a/README.md +++ b/README.md @@ -27,24 +27,130 @@ individually. ## This solution -// TODO +This is a [babel-macro][babel-macros] which allows you to import files that +match a glob. It supports `import` statements for synchronous resolution as well +as dynamic `import()` for deferred resolution (for code splitting with react +router for example). ## Installation This module is distributed via [npm][npm] which is bundled with [node][node] and -should be installed as one of your project's `dependencies`: +should be installed as one of your project's `devDependencies`: ``` -npm install --save import-all.macro +npm install --save-dev import-all.macro ``` ## Usage -// TODO +Once you've [configured `babel-macros`][babel-macros-user] you can +import/require `import-all.macro`. + +The `importAll` functions accept a [`glob`][glob] and will transpile your code +to import statements/dynamic imports for each file that matches the given glob. + +Let's imagine you have a directory called `my-files` with the files +`a.js`, `b.js`, `c.js`, and `d.js`. + +Here are a few before/after examples: + + + + + +**`importAll` uses dynamic import** + +```javascript +import importAll from 'import-all.macro' + +document.getElementById('load-stuff').addEventListener(() => { + importAll('./my-files/*.js').then(all => { + console.log(all) + }) +}) + + ↓ ↓ ↓ ↓ ↓ ↓ + +document.getElementById('load-stuff').addEventListener(() => { + Promise.all([ + import('./my-files/a.js'), + import('./my-files/b.js'), + import('./my-files/c.js'), + import('./my-files/d.js'), + ]) + .then(function importAllHandler(importVals) { + return { + a: importVals[0], + b: importVals[1], + c: importVals[2], + d: importVals[3], + } + }) + .then(all => { + console.log(all) + }) +}) +``` + +**`importAll.sync` uses static imports** + +```javascript +import importAll from 'import-all.macro' + +const a = importAll.sync('./my-files/*.js') + + ↓ ↓ ↓ ↓ ↓ ↓ + +import * as _a from './my-files/a.js' +import * as _b from './my-files/b.js' +import * as _c from './my-files/c.js' +import * as _d from './my-files/d.js' + +const a = { + a: _a, + b: _b, + c: _c, + d: _d, +} +``` + +**`importAll.deferred` gives an object with dynamic imports** + +```javascript +import importAll from 'import-all.macro' + +const routes = importAll.deferred('./my-files/*.js') + + ↓ ↓ ↓ ↓ ↓ ↓ + +const routes = { + a: function a() { + return import('./my-files/a.js') + }, + b: function b() { + return import('./my-files/b.js') + }, + c: function c() { + return import('./my-files/c.js') + }, + d: function d() { + return import('./my-files/d.js') + }, +} +``` + + + +## Caveats + +Some static analysis tools (like ESLint, Flow, and Jest) wont like this very much +without a little additional work. So Jest's watch mode may not pick up all your +tests that are relevant based on changes and some ESLint plugins +(like `eslint-plugin-import`) will probably fail on this. ## Inspiration -// TODO +[Sunil Pai's tweet][sunil-tweet] ## Other Solutions @@ -94,3 +200,6 @@ MIT [twitter-badge]: https://img.shields.io/twitter/url/https/github.com/kentcdodds/import-all.macro.svg?style=social [emojis]: https://github.com/kentcdodds/all-contributors#emoji-key [all-contributors]: https://github.com/kentcdodds/all-contributors +[babel-macros-user]: https://github.com/kentcdodds/babel-macros/blob/master/other/docs/user.md +[glob]: https://www.npmjs.com/package/glob +[sunil-tweet]: https://twitter.com/threepointone/status/908290510225330176 diff --git a/lint-staged.config.js b/lint-staged.config.js new file mode 100644 index 0000000..3d50b7f --- /dev/null +++ b/lint-staged.config.js @@ -0,0 +1,5 @@ +const lintStagedConfig = require('kcd-scripts/config').lintStaged + +module.exports = Object.assign(lintStagedConfig.linters, { + '**/__snapshots__/**': 'node other/snap-to-readme.js', +}) diff --git a/other/snap-to-readme.js b/other/snap-to-readme.js new file mode 100644 index 0000000..d7af895 --- /dev/null +++ b/other/snap-to-readme.js @@ -0,0 +1,41 @@ +const path = require('path') +const fs = require('fs') + +const readmePath = path.join(__dirname, '../README.md') +const readme = fs.readFileSync(readmePath, 'utf8') +const snaps = require('../src/__tests__/__snapshots__/macro.js.snap') + +const snapNumberRegex = /^README:(\d+)/ +const snapTitleRegex = /^README:\d+(.*) \d+$/ +const readmeSnapRegex = /[\s\S]*?/ +const snapsToUse = Object.keys(snaps) + .filter(name => name.startsWith('README')) + .sort( + (nameA, nameB) => + Number(nameA.match(snapNumberRegex)[1]) > + Number(nameB.match(snapNumberRegex)[1]) + ? 1 + : 0, + ) + .reduce((acc, key) => { + const title = key.match(snapTitleRegex)[1].trim() + const contents = snaps[key].trim() + return [acc, `**${title}**`, `~~~javascript\n${contents}\n~~~`] + .join('\n\n') + .replace(/~/g, '`') + }, []) + +const newReadme = readme.replace( + readmeSnapRegex, + ` + + + + +${snapsToUse.trim()} + + + `.trim(), +) + +fs.writeFileSync(readmePath, newReadme) diff --git a/package.json b/package.json index 28cb27e..71d30f4 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,8 @@ "devDependencies": { "ast-pretty-print": "^2.0.1", "babel-plugin-tester": "^4.0.0", - "kcd-scripts": "^0.16.0" + "kcd-scripts": "^0.16.0", + "prettier": "^1.7.0" }, "eslintConfig": { "extends": "./node_modules/kcd-scripts/eslint.js" diff --git a/src/__tests__/__snapshots__/macro.js.snap b/src/__tests__/__snapshots__/macro.js.snap index 82f5542..17bd8ca 100644 --- a/src/__tests__/__snapshots__/macro.js.snap +++ b/src/__tests__/__snapshots__/macro.js.snap @@ -1,11 +1,11 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`importAll uses dynamic import 1`] = ` +exports[`README:1 \`importAll\` uses dynamic import 1`] = ` -import importAll from '../macro' +import importAll from 'import-all.macro' document.getElementById('load-stuff').addEventListener(() => { - importAll('./fixtures/*.js').then(all => { + importAll('./my-files/*.js').then(all => { console.log(all) }) }) @@ -13,71 +13,80 @@ document.getElementById('load-stuff').addEventListener(() => { ↓ ↓ ↓ ↓ ↓ ↓ document.getElementById('load-stuff').addEventListener(() => { - Promise.all([import('./fixtures/a.js'), import('./fixtures/b.js'), import('./fixtures/c.js'), import('./fixtures/d.js')]).then(function importAllHandler(importVals) { - return { - 'a': importVals[0], - 'b': importVals[1], - 'c': importVals[2], - 'd': importVals[3] - }; - }).then(all => { - console.log(all); - }); -}); + Promise.all([ + import('./my-files/a.js'), + import('./my-files/b.js'), + import('./my-files/c.js'), + import('./my-files/d.js'), + ]) + .then(function importAllHandler(importVals) { + return { + a: importVals[0], + b: importVals[1], + c: importVals[2], + d: importVals[3], + } + }) + .then(all => { + console.log(all) + }) +}) + `; -exports[`importAll.deferred gives an object with dynamic imports 1`] = ` +exports[`README:2 \`importAll.sync\` uses static imports 1`] = ` -import importAll from '../macro' +import importAll from 'import-all.macro' -const routes = importAll.deferred('./fixtures/*.js') +const a = importAll.sync('./my-files/*.js') ↓ ↓ ↓ ↓ ↓ ↓ -const routes = { - 'a': function a() { - return import('./fixtures/a.js'); - }, - 'b': function b() { - return import('./fixtures/b.js'); - }, - 'c': function c() { - return import('./fixtures/c.js'); - }, - 'd': function d() { - return import('./fixtures/d.js'); - } -}; +import * as _a from './my-files/a.js' +import * as _b from './my-files/b.js' +import * as _c from './my-files/c.js' +import * as _d from './my-files/d.js' + +const a = { + a: _a, + b: _b, + c: _c, + d: _d, +} + `; -exports[`importAll.sync uses static imports 1`] = ` +exports[`README:3 \`importAll.deferred\` gives an object with dynamic imports 1`] = ` -import importAll from '../macro' +import importAll from 'import-all.macro' -const a = importAll.sync('./fixtures/*.js') +const routes = importAll.deferred('./my-files/*.js') ↓ ↓ ↓ ↓ ↓ ↓ -import * as _a from './fixtures/a.js'; -import * as _b from './fixtures/b.js'; -import * as _c from './fixtures/c.js'; -import * as _d from './fixtures/d.js'; - +const routes = { + a: function a() { + return import('./my-files/a.js') + }, + b: function b() { + return import('./my-files/b.js') + }, + c: function c() { + return import('./my-files/c.js') + }, + d: function d() { + return import('./my-files/d.js') + }, +} -const a = { - 'a': _a, - 'b': _b, - 'c': _c, - 'd': _d -}; `; exports[`no usage 1`] = ` -import importAll from '../macro' +import importAll from 'import-all.macro' ↓ ↓ ↓ ↓ ↓ ↓ diff --git a/src/__tests__/macro.js b/src/__tests__/macro.js index 8f3a047..79ecd8d 100644 --- a/src/__tests__/macro.js +++ b/src/__tests__/macro.js @@ -1,12 +1,18 @@ import path from 'path' import pluginTester from 'babel-plugin-tester' import plugin from 'babel-macros' +import prettier from 'prettier' +import {prettier as prettierConfig} from 'kcd-scripts/config' const projectRoot = path.join(__dirname, '../../') expect.addSnapshotSerializer({ print(val) { - return val.split(projectRoot).join('/') + return val + .split(projectRoot) + .join('/') + .replace(/fixtures/g, 'my-files') + .replace(/..\/macro/, 'import-all.macro') }, test(val) { return typeof val === 'string' @@ -19,14 +25,15 @@ pluginTester({ babelOptions: { filename: __filename, }, + formatResult(result) { + return prettier.format( + result, + Object.assign({trailingComma: 'es5'}, prettierConfig), + ) + }, tests: { 'no usage': `import importAll from '../macro'`, - 'importAll.sync uses static imports': ` - import importAll from '../macro' - - const a = importAll.sync('./fixtures/*.js') - `, - 'importAll uses dynamic import': ` + 'README:1 `importAll` uses dynamic import': ` import importAll from '../macro' document.getElementById('load-stuff').addEventListener(() => { @@ -35,7 +42,12 @@ pluginTester({ }) }) `, - 'importAll.deferred gives an object with dynamic imports': ` + 'README:2 `importAll.sync` uses static imports': ` + import importAll from '../macro' + + const a = importAll.sync('./fixtures/*.js') + `, + 'README:3 `importAll.deferred` gives an object with dynamic imports': ` import importAll from '../macro' const routes = importAll.deferred('./fixtures/*.js')