Skip to content

Commit 5831bb5

Browse files
committed
feat(node): toggle
Signed-off-by: Lexus Drumgold <[email protected]>
1 parent 01e120b commit 5831bb5

File tree

7 files changed

+201
-2
lines changed

7 files changed

+201
-2
lines changed

.eslintrc.cjs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,15 @@
1111
const config = {
1212
root: true,
1313
extends: ['./.eslintrc.base.cjs'],
14-
overrides: [...require('./.eslintrc.base.cjs').overrides]
14+
overrides: [
15+
...require('./.eslintrc.base.cjs').overrides,
16+
{
17+
files: ['./src/toggle.ts'],
18+
rules: {
19+
'unicorn/prefer-json-parse-buffer': 0
20+
}
21+
}
22+
]
1523
}
1624

1725
module.exports = config

__mocks__/.gitkeep

Whitespace-only changes.

__mocks__/fs.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/**
2+
* @file Mocks - fs
3+
* @module mocks/fs
4+
* @see https://nodejs.org/docs/latest-v16.x/api/fs.html
5+
*/
6+
7+
import volume from '#fixtures/volume'
8+
import type { TDataOut } from 'memfs/lib/encoding'
9+
import type { IWriteFileOptions, TData, TFileId } from 'memfs/lib/volume'
10+
11+
/**
12+
* Synchronously returns the contents of `path`.
13+
*
14+
* @see https://nodejs.org/docs/latest-v16.x/api/fs.html#fsreadfilesyncpath-options
15+
*
16+
* @param {TFileId} path - Filename or file descriptor
17+
* @param {IWriteFileOptions | string} [options] - Read file options
18+
* @return {TDataOut} File content
19+
*/
20+
export const readFileSync = vi.fn(volume.readFileSync.bind(volume))
21+
22+
/**
23+
* Synchronously writes `data` to `file`, replacing `file` if it already exists.
24+
*
25+
* @see https://nodejs.org/docs/latest-v16.x/api/fs.html#fswritefilesyncfile-data-options
26+
*
27+
* @param {TFileId} file - Filename or file descriptor
28+
* @param {TData} data - File content
29+
* @param {IWriteFileOptions} [options] - Write file options
30+
* @return {Promise<void>} Nothing when complete
31+
*/
32+
export const writeFileSync = vi.fn(volume.writeFileSync.bind(volume))
33+
34+
export default {
35+
readFileSync,
36+
writeFileSync
37+
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@
143143
"trash-cli": "5.0.0",
144144
"ts-dedent": "2.2.0",
145145
"ts-node": "10.9.1",
146+
"type-fest": "3.0.0",
146147
"typescript": "4.8.4",
147148
"version-bump-prompt": "6.1.0",
148149
"vite": "3.1.4",
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/**
2+
* @file Functional Tests - toggle
3+
* @module toggle-pkg-type/tests/toggle/functional
4+
*/
5+
6+
import vfs from '#fixtures/volume'
7+
import type { PackageJson } from 'type-fest'
8+
import testSubject from '../toggle'
9+
10+
vi.mock('node:fs')
11+
12+
describe('functional:toggle', () => {
13+
afterEach(() => {
14+
vfs.reset()
15+
})
16+
17+
it('should do nothing if package type is undefined', () => {
18+
// Arrange
19+
const pkg: PackageJson = { name: 'foo', version: '1.0.0' }
20+
const pkgstring: string = JSON.stringify(pkg, null, 2) + '\n'
21+
vfs.writeFileSync('./package.json', pkgstring)
22+
23+
// Act
24+
testSubject()
25+
26+
// Expect
27+
expect(vfs.readFileSync('./package.json', 'utf8')).to.equal(pkgstring)
28+
})
29+
30+
describe('disable', () => {
31+
const pkg: PackageJson = { type: 'module' }
32+
33+
beforeEach(() => {
34+
vfs.writeFileSync('./package.json', JSON.stringify(pkg, null, 2) + '\n')
35+
})
36+
37+
it('should disable package type with "off"', () => {
38+
// Act
39+
testSubject('off')
40+
41+
// Expect
42+
expect(JSON.parse(vfs.readFileSync('./package.json', 'utf8') as string))
43+
.to.have.property('#type')
44+
.that.equals(pkg.type)
45+
})
46+
47+
it('should disable package type without command', () => {
48+
// Act
49+
testSubject()
50+
51+
// Expect
52+
expect(JSON.parse(vfs.readFileSync('./package.json', 'utf8') as string))
53+
.to.have.property('#type')
54+
.that.equals(pkg.type)
55+
})
56+
})
57+
58+
describe('enable', () => {
59+
const pkg: PackageJson = { '#type': 'module' }
60+
61+
beforeEach(() => {
62+
vfs.writeFileSync('./package.json', JSON.stringify(pkg, null, 2) + '\n')
63+
})
64+
65+
it('should enable package type with "on"', () => {
66+
// Act
67+
testSubject('on')
68+
69+
// Expect
70+
expect(JSON.parse(vfs.readFileSync('./package.json', 'utf8') as string))
71+
.to.have.property('type')
72+
.that.equals(pkg['#type'])
73+
})
74+
75+
it('should enable package type without command', () => {
76+
// Act
77+
testSubject()
78+
79+
// Expect
80+
expect(JSON.parse(vfs.readFileSync('./package.json', 'utf8') as string))
81+
.to.have.property('type')
82+
.that.equals(pkg['#type'])
83+
})
84+
})
85+
})

src/toggle.ts

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,64 @@
33
* @module toggle-pkg-type/toggle
44
*/
55

6-
export default {}
6+
import fs from 'node:fs'
7+
import path from 'node:path'
8+
import type { PackageJson } from 'type-fest'
9+
10+
/**
11+
* Enables or disables [`type`][1] in `package.json`.
12+
*
13+
* [1]: https://nodejs.org/api/packages.html#type
14+
*
15+
* @example
16+
* toggle()
17+
* @example
18+
* toggle('off')
19+
* @example
20+
* toggle('on')
21+
*
22+
* @param {'off' | 'on'} [command] - Toggle command
23+
* @return {void} Nothing when complete
24+
*/
25+
function toggle(command?: 'off' | 'on'): void {
26+
// see: https://yarnpkg.com/advanced/lifecycle-scripts#environment-variables
27+
const { npm_package_json = 'package.json' } = process.env
28+
29+
/**
30+
* Absolute path to `package.json`.
31+
*
32+
* @const {string} pkgfile
33+
*/
34+
const pkgfile: string = path.resolve(npm_package_json)
35+
36+
/**
37+
* `package.json` data.
38+
*
39+
* @var {PackageJson} pkg
40+
*/
41+
let pkg: PackageJson = JSON.parse(fs.readFileSync(pkgfile, 'utf8'))
42+
43+
// toggle package type
44+
pkg = Object.keys(pkg).reduce<PackageJson>((acc, key) => {
45+
const [, type, prefix = ''] = /^((#?)type)$/.exec(key) ?? []
46+
47+
if (type) {
48+
key = command
49+
? `${command === 'off' ? '#' : ''}type`
50+
: prefix
51+
? type.replace(new RegExp('^' + prefix), '')
52+
: '#' + type
53+
54+
acc[key] = pkg[type]!
55+
} else {
56+
acc[key] = pkg[key]!
57+
}
58+
59+
return acc
60+
}, {})
61+
62+
// rewrite package.json
63+
return void fs.writeFileSync(pkgfile, JSON.stringify(pkg, null, 2) + '\n')
64+
}
65+
66+
export default toggle

yarn.lock

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -887,6 +887,7 @@ __metadata:
887887
trash-cli: "npm:5.0.0"
888888
ts-dedent: "npm:2.2.0"
889889
ts-node: "npm:10.9.1"
890+
type-fest: "npm:3.0.0"
890891
typescript: "npm:4.8.4"
891892
version-bump-prompt: "npm:6.1.0"
892893
vite: "npm:3.1.4"
@@ -7719,6 +7720,13 @@ __metadata:
77197720
languageName: node
77207721
linkType: hard
77217722

7723+
"type-fest@npm:3.0.0":
7724+
version: 3.0.0
7725+
resolution: "type-fest@npm:3.0.0"
7726+
checksum: 9a168676bf3d1c2e063ae2a25cec083aa929ff3a38505ee1131ef088b343017dd40b2747d5b9c4fb5879b0afc725b839643797cec5a2cec01889ade71b975e49
7727+
languageName: node
7728+
linkType: hard
7729+
77227730
"type-fest@npm:^0.18.0":
77237731
version: 0.18.1
77247732
resolution: "type-fest@npm:0.18.1"

0 commit comments

Comments
 (0)