Skip to content

Commit 905d47c

Browse files
committed
feat: support nested aliases
1 parent a4eb5aa commit 905d47c

File tree

7 files changed

+134
-71
lines changed

7 files changed

+134
-71
lines changed

README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# generate-export-aliases
2+
23
[![NPM Package](https://badge.fury.io/js/generate-export-aliases.svg)](https://www.npmjs.com/package/generate-export-aliases)
34
[![Build Status](https://travis-ci.org/patrickhulce/generate-export-aliases.svg?branch=master)](https://travis-ci.org/patrickhulce/generate-export-aliases)
45
[![Coverage Status](https://coveralls.io/repos/github/patrickhulce/generate-export-aliases/badge.svg?branch=master)](https://coveralls.io/github/patrickhulce/generate-export-aliases?branch=master)
@@ -7,18 +8,19 @@
78

89
Generates additional files to make requiring files deep in your node module easy and safe from refactoring.
910

10-
1111
## Usage
1212

1313
Save `generate-export-aliases` as a dev dependency in your `package.json`.
14+
1415
```sh
15-
yarn add -D generate-export-aliases
16+
npm i -D generate-export-aliases
1617
```
1718

1819
Add a prepublish hook and the exports you wish to alias to the `config` section of your `package.json` under `exportAliases`.
1920
For example, if you wanted to alias the `myHelper.js` file in the following directory structure...
2021

2122
#### Example Folder Structure
23+
2224
```
2325
├── LICENSE
2426
├── README.md
@@ -33,6 +35,7 @@ For example, if you wanted to alias the `myHelper.js` file in the following dire
3335
```
3436

3537
#### `package.json`
38+
3639
```json
3740
{
3841
"name": "my-fantastic-library",
@@ -48,11 +51,13 @@ For example, if you wanted to alias the `myHelper.js` file in the following dire
4851
```
4952

5053
#### Requiring Your Alias
54+
5155
```js
5256
const exposedHelper = require('my-fantastic-library/exposed-helper')
5357
const exposedHelperOriginal = require('my-fantastic-library/lib/shared/myHelper.js')
5458
exposedHelper === exposedHelperOriginal // true
5559
```
5660

5761
## Inspiration
62+
5863
[lodash](https://github.com/lodash/lodash)'s build process

lib/aliases.js

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@ const fs = require('fs')
22
const path = require('path')
33

44
const _ = require('lodash')
5-
const Promise = require('bluebird')
6-
7-
Promise.promisifyAll(fs)
85

96
function findSource(rootDir, file) {
107
const filePath = path.join(rootDir, file)
@@ -17,39 +14,53 @@ function findSource(rootDir, file) {
1714

1815
module.exports = function (rootDir) {
1916
return {
20-
getAliases() {
21-
return fs.readFileAsync(path.join(rootDir, 'package.json'), 'utf8').then(data => {
22-
const pkg = JSON.parse(data)
23-
const config = pkg.config || {}
24-
return _.map(config.exportAliases || {}, (value, key) => {
25-
if (value.indexOf('./') !== 0) {
26-
value = './' + value
27-
}
28-
return {alias: key, source: value}
29-
})
17+
async getAliases() {
18+
const data = fs.readFileSync(path.join(rootDir, 'package.json'), 'utf8')
19+
const pkg = JSON.parse(data)
20+
const config = pkg.config || {}
21+
return _.map(config.exportAliases || {}, (value, key) => {
22+
return {
23+
alias: key,
24+
source: value.replace(/^\.\//, '').replace(/\/$/, ''),
25+
isIndex: value.endsWith('/')
26+
}
3027
})
3128
},
32-
createAlias(source, alias) {
33-
if (_.includes(alias, '*') || _.includes(alias, '/')) {
29+
async createAlias(source, alias) {
30+
if (_.includes(alias, '*')) {
3431
return Promise.reject(new Error('nested and wildcard aliases not yet supported'))
3532
}
3633

37-
const sourceJsPath = findSource(rootDir, source)
34+
const shouldUseIndexFolder = alias.isIndex
35+
const [aliasName, ...aliasFolderPathParts] = alias.split('/').reverse()
36+
const aliasFolderPath = path.join(
37+
rootDir,
38+
aliasFolderPathParts.join('/'),
39+
shouldUseIndexFolder ? aliasName : '',
40+
)
41+
fs.mkdirSync(aliasFolderPath, {recursive: true})
42+
const levelsOfNesting = aliasFolderPath.split('/').length - 1
43+
const pathToSource = levelsOfNesting
44+
? '../'.repeat(levelsOfNesting) + source
45+
: './' + source
46+
47+
const sourceJsPath = findSource(rootDir, './' + source)
3848
const sourceRaw = fs.readFileSync(sourceJsPath).toString()
3949
// eslint-disable-next-line unicorn/prefer-starts-ends-with
4050
const maybeIsEsm = sourceRaw.match(/^export /m)
4151
const sourceDTsPath = sourceJsPath.replace(/\.js$/, '.d.ts')
42-
const destJsPath = path.join(rootDir, alias + '.js')
43-
const destDTsPath = path.join(rootDir, alias + '.d.ts')
52+
const fileName = shouldUseIndexFolder ? 'index' : aliasName
53+
const destJsPath = path.join(aliasFolderPath, fileName + '.js')
54+
const destDTsPath = path.join(aliasFolderPath, fileName + '.d.ts')
4455
if (fs.existsSync(sourceDTsPath)) {
45-
fs.writeFileSync(destDTsPath, `export * from '${source}'\n`, 'utf8')
56+
fs.writeFileSync(destDTsPath, `export * from '${pathToSource}'\n`, 'utf8')
4657
}
4758

48-
const contents = maybeIsEsm ?
49-
`export * from "${source}"\n` :
50-
`module.exports = require("${source}")\n`
59+
const contents = maybeIsEsm
60+
? `export * from "${pathToSource}"\n`
61+
: `module.exports = require("${pathToSource}")\n`
5162

52-
return fs.writeFileAsync(destJsPath, contents, 'utf8')
63+
return fs.writeFileSync(destJsPath, contents, 'utf8')
5364
},
5465
}
5566
}

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
"generate-export-aliases": "./bin/generate-aliases.js"
77
},
88
"scripts": {
9-
"test": "npm run test:lint && npm run test:unit",
10-
"test:lint": "lint",
9+
"test": "npm run test:unit",
1110
"test:unit": "mocha --reporter spec test/*.test.js",
1211
"test:coverage": "istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- test/*.test.js",
1312
"semantic-release": "semantic-release"

test/aliases.test.js

Lines changed: 76 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,24 @@ describe('lib/aliases.js', () => {
1212

1313
describe('#getAliases', () => {
1414
it('should read the aliases from package.json config', () => {
15-
return aliasUtils(app1Dir).getAliases().then(aliases => {
16-
aliases.should.eql([
17-
{alias: 'myFile', source: './lib/fileA.js'},
18-
{alias: 'other', source: './lib/other'},
19-
])
20-
})
15+
return aliasUtils(app1Dir)
16+
.getAliases()
17+
.then(aliases => {
18+
aliases.should.eql([
19+
{alias: 'myFile', source: 'lib/fileA.js', isIndex: false},
20+
{alias: 'other/', source: 'lib/other', isIndex: true},
21+
{alias: 'nested/cjs/folder/path', source: 'lib/fileA.js', isIndex: true},
22+
{alias: 'nested/esm/folder/path', source: 'lib/fileB.esm.js', isIndex: false},
23+
])
24+
})
2125
})
2226

2327
it('should default to no aliases', () => {
24-
return aliasUtils(app2Dir + '/').getAliases().then(aliases => {
25-
aliases.should.eql([])
26-
})
28+
return aliasUtils(app2Dir + '/')
29+
.getAliases()
30+
.then(aliases => {
31+
aliases.should.eql([])
32+
})
2733
})
2834

2935
it('should fail when no package.json is found', () => {
@@ -33,52 +39,83 @@ describe('lib/aliases.js', () => {
3339

3440
describe('#createAlias', () => {
3541
it('should create a file in the root dir', () => {
36-
return aliasUtils(app3Dir).createAlias('file', 'alias').then(() => {
37-
return Promise.try(() => {
38-
fs.statSync(app3Dir + '/alias.js')
39-
fs.unlinkSync(app3Dir + '/alias.js')
42+
return aliasUtils(app3Dir)
43+
.createAlias('file', 'alias')
44+
.then(() => {
45+
return Promise.try(() => {
46+
fs.statSync(app3Dir + '/alias.js')
47+
fs.unlinkSync(app3Dir + '/alias.js')
48+
})
4049
})
41-
})
4250
})
4351

4452
it('should create .d.ts files when appropriate', () => {
45-
return aliasUtils(app1Dir).createAlias('lib/other', 'alias').then(() => {
46-
return Promise.try(() => {
47-
const contents = fs.readFileSync(app1Dir + '/alias.d.ts', 'utf8')
48-
contents.should.contain('export * from')
49-
fs.unlinkSync(app1Dir + '/alias.js')
50-
fs.unlinkSync(app1Dir + '/alias.d.ts')
53+
return aliasUtils(app1Dir)
54+
.createAlias('lib/other', 'alias')
55+
.then(() => {
56+
return Promise.try(() => {
57+
const contents = fs.readFileSync(app1Dir + '/alias.d.ts', 'utf8')
58+
contents.should.contain('export * from')
59+
fs.unlinkSync(app1Dir + '/alias.js')
60+
fs.unlinkSync(app1Dir + '/alias.d.ts')
61+
})
5162
})
52-
})
5363
})
5464

5565
it('should create an esm file when source looks like esm', () => {
56-
return aliasUtils(app1Dir).createAlias('lib/fileB.esm.js', 'alias').then(() => {
57-
return Promise.try(() => {
58-
const contents = fs.readFileSync(app1Dir + '/alias.js', 'utf8')
59-
contents.should.match(/^export \* from/)
60-
fs.unlinkSync(app1Dir + '/alias.js')
66+
return aliasUtils(app1Dir)
67+
.createAlias('lib/fileB.esm.js', 'alias')
68+
.then(() => {
69+
return Promise.try(() => {
70+
const contents = fs.readFileSync(app1Dir + '/alias.js', 'utf8')
71+
contents.should.match(/^export \* from ".\/lib\/fileB"/)
72+
fs.unlinkSync(app1Dir + '/alias.js')
73+
})
6174
})
62-
})
6375
})
6476

65-
it('should point the alias to the source file', () => {
66-
return aliasUtils(app3Dir).createAlias('./file.js', 'alias').then(() => {
67-
return Promise.try(() => {
68-
const original = require(app3Dir + '/file.js')
69-
const aliased = require(app3Dir + '/alias.js')
70-
original.should.equal(aliased)
71-
fs.unlinkSync(app3Dir + '/alias.js')
77+
it('should create an index file when alias ends in /', () => {
78+
return aliasUtils(app1Dir)
79+
.createAlias('lib/fileB.esm.js', 'alias')
80+
.then(() => {
81+
return Promise.try(() => {
82+
const contents = fs.readFileSync(app1Dir + '/alias.js', 'utf8')
83+
contents.should.match(/^export \* from "..\/lib\/fileB"/)
84+
fs.unlinkSync(app1Dir + '/alias.js')
85+
})
7286
})
73-
})
7487
})
7588

76-
it('should fail for missing files', () => {
77-
return (() => aliasUtils(app3Dir).createAlias('./missing', 'alias')).should.throw(/Cannot find/)
89+
it('should create nested aliases', () => {
90+
const expectedPath = app1Dir + '/nested/folder/alias.js'
91+
return aliasUtils(app1Dir)
92+
.createAlias('lib/fileB.esm.js', 'nested/folder/alias')
93+
.then(() => {
94+
return Promise.try(() => {
95+
const contents = fs.readFileSync(expectedPath, 'utf8')
96+
contents.should.match(/^export \* from "..\/..\/lib\/fileB"/)
97+
fs.unlinkSync(expectedPath)
98+
})
99+
})
78100
})
79101

80-
it('should fail for nested aliases', () => {
81-
return aliasUtils(app3Dir).createAlias('./file', 'nested/alias').should.eventually.be.rejected
102+
it('should point the alias to the source file', () => {
103+
return aliasUtils(app3Dir)
104+
.createAlias('./file.js', 'alias')
105+
.then(() => {
106+
return Promise.try(() => {
107+
const original = require(app3Dir + '/file.js')
108+
const aliased = require(app3Dir + '/alias.js')
109+
original.should.equal(aliased)
110+
fs.unlinkSync(app3Dir + '/alias.js')
111+
})
112+
})
113+
})
114+
115+
it('should fail for missing files', () => {
116+
return (() => aliasUtils(app3Dir).createAlias('./missing', 'alias')).should.throw(
117+
/Cannot find/,
118+
)
82119
})
83120

84121
it('should fail for wildcard aliases', () => {

test/fixtures/app1/lib/fileA.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = 'Hello, World!'

test/fixtures/app1/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
"config": {
33
"exportAliases": {
44
"myFile": "./lib/fileA.js",
5-
"other": "lib/other"
5+
"other/": "lib/other",
6+
"nested/cjs/folder/path/": "lib/fileA.js",
7+
"nested/esm/folder/path": "lib/fileB.esm.js"
68
}
79
}
810
}

test/generate.test.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,19 @@ describe('lib/generate.js', () => {
1212
return generate(app1Dir).then(() => {
1313
return Promise.try(() => {
1414
fs.statSync(app1Dir + '/myFile.js')
15-
fs.statSync(app1Dir + '/other.js')
16-
fs.statSync(app1Dir + '/other.d.ts')
15+
fs.statSync(app1Dir + '/other/index.js')
16+
fs.statSync(app1Dir + '/other/index.d.ts')
17+
fs.statSync(app1Dir + '/nested/cjs/folder/path/index.js')
18+
fs.statSync(app1Dir + '/nested/esm/folder/path.js')
19+
20+
require(app1Dir + '/myFile.js').should.equal('Hello, World!')
21+
require(app1Dir + '/nested/cjs/folder/path/index.js').should.equal('Hello, World!')
22+
1723
fs.unlinkSync(app1Dir + '/myFile.js')
18-
fs.unlinkSync(app1Dir + '/other.d.ts')
19-
fs.unlinkSync(app1Dir + '/other.js')
24+
fs.unlinkSync(app1Dir + '/other/index.js')
25+
fs.unlinkSync(app1Dir + '/other/index.d.ts')
26+
fs.unlinkSync(app1Dir + '/nested/cjs/folder/path/index.js')
27+
fs.unlinkSync(app1Dir + '/nested/esm/folder/path.js')
2028
})
2129
})
2230
})

0 commit comments

Comments
 (0)