Skip to content
This repository was archived by the owner on Dec 30, 2023. It is now read-only.

Commit 40625dc

Browse files
authored
feat(runtime): ✨ Add a a new module module (#34)
1 parent e9bca34 commit 40625dc

File tree

8 files changed

+319
-161
lines changed

8 files changed

+319
-161
lines changed

.changeset/khaki-trainers-impress.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@terminal-nerds/snippets-runtime": minor
3+
---
4+
5+
✨ Added a module `module` for snippets related to module, dependencies and packages.

package.json

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,21 +54,23 @@
5454
"@types/node": "18.13.0",
5555
"browserslist": "4.21.5",
5656
"bun-types": "0.5.7",
57-
"concurrently": "7.6.0",
58-
"del-cli": "5.0.0",
5957
"depcheck": "1.4.3",
6058
"eslint": "8.33.0",
61-
"husky": "8.0.3",
62-
"lint-staged": "13.1.0",
6359
"markdownlint-cli": "0.33.0",
6460
"prettier": "2.8.3",
65-
"prettier-plugin-sort-markdown-table": "^1.0.1",
61+
"prettier-plugin-sort-markdown-table": "1.0.2",
6662
"pretty-quick": "3.1.3",
6763
"syncpack": "8.4.11",
6864
"tsup": "6.5.0",
69-
"tsx": "^3.12.3",
7065
"turbo": "1.7.4",
7166
"typescript": "4.9.5",
7267
"vitest": "0.28.4"
68+
},
69+
"optionalDependencies": {
70+
"concurrently": "7.6.0",
71+
"del-cli": "5.0.0",
72+
"husky": "8.0.3",
73+
"lint-staged": "13.1.0",
74+
"tsx": "3.12.3"
7375
}
7476
}

packages/runtime/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
| Name | Size |
2121
| ------------------------------------------------ | --------------------------------------------------------------- |
2222
| [`@terminal-nerds/snippets-runtime/environment`] | ![environment size gzip badge] ![environment size brotli badge] |
23+
| [`@terminal-nerds/snippets-runtime/module`] | ![module size gzip badge] ![module size brotli badge] |
2324
| [`@terminal-nerds/snippets-runtime/scope`] | ![scope size gzip badge] ![scope size brotli badge] |
2425
| [`@terminal-nerds/snippets-runtime/variable`] | ![variable size gzip badge] ![variable size brotli badge] |
2526

@@ -29,6 +30,10 @@
2930
[environment size gzip badge]: https://badgen.net/badgesize/gzip/file-url/unpkg.com/@terminal-nerds/snippets-runtime/dist/environment/environment.js?label=gzip
3031
[environment size brotli badge]: https://badgen.net/badgesize/brotli/file-url/unpkg.com/@terminal-nerds/snippets-runtime/dist/environment/environment.js?label=brotli
3132

33+
[`@terminal-nerds/snippets-runtime/module`]: https://github.com/terminal-nerds/snippets/blob/main/packages/runtime/source/module/module.ts
34+
[module size gzip badge]: https://badgen.net/badgesize/gzip/file-url/unpkg.com/@terminal-nerds/snippets-runtime/dist/module/module.js?label=gzip
35+
[module size brotli badge]: https://badgen.net/badgesize/brotli/file-url/unpkg.com/@terminal-nerds/snippets-runtime/dist/module/module.js?label=brotli
36+
3237
[`@terminal-nerds/snippets-runtime/scope`]: https://github.com/terminal-nerds/snippets/blob/main/packages/runtime/source/scope/scope.ts
3338
[scope size gzip badge]: https://badgen.net/badgesize/gzip/file-url/unpkg.com/@terminal-nerds/snippets-runtime/dist/scope/scope.js?label=gzip
3439
[scope size brotli badge]: https://badgen.net/badgesize/brotli/file-url/unpkg.com/@terminal-nerds/snippets-runtime/dist/scope/scope.js?label=brotli

packages/runtime/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@
6464
},
6565
"dependencies": {
6666
"@terminal-nerds/snippets-error": "workspace:*",
67+
"local-pkg": "^0.4.3",
68+
"read-pkg-up": "^9.1.0",
69+
"type-fest": "3.5.7",
6770
"zod": "3.20.6"
6871
},
6972
"peerDependenciesMeta": {
@@ -72,8 +75,8 @@
7275
}
7376
},
7477
"devDependencies": {
75-
"@terminal-nerds/snippets-test": "workspace:*",
7678
"@edge-runtime/vm": "2.1.2",
79+
"@terminal-nerds/snippets-test": "workspace:*",
7780
"happy-dom": "8.9.0",
7881
"jsdom": "21.1.0"
7982
}

packages/runtime/source/main.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/* MODULES */
22
export * from "./environment/environment.js";
3+
export * from "./module/module.js";
34
export * from "./scope/scope.js";
45
export * from "./variable/variable.js";
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { returns } from "@terminal-nerds/snippets-test/unit";
2+
import { describe, expect, it } from "vitest";
3+
4+
import { getDependenciesMap, getPackagesNames, hasModule } from "./module.js";
5+
6+
describe(`hasModule(name)`, () => {
7+
const nonExistingModuleName = "terminal-nerdss";
8+
9+
it(returns(false).on(`non-existing module`).sample(nonExistingModuleName), () => {
10+
expect(hasModule(nonExistingModuleName)).toBe(false);
11+
});
12+
13+
const moduleName = "typescript";
14+
15+
it(returns(true).on(`existing module`).sample(moduleName), () => {
16+
expect(hasModule(moduleName)).toBe(true);
17+
});
18+
});
19+
20+
describe(`getDependenciesMap()`, () => {
21+
const map = getDependenciesMap();
22+
23+
it(returns(map).on(`the current package directory`), () => {
24+
expect(map).toBeInstanceOf(Map);
25+
});
26+
27+
const expectedKeys = ["dependencies", "devDependencies", "optionalDependencies", "peerDependencies"] as const;
28+
29+
it(`has keys: ${JSON.stringify(expectedKeys, undefined, 1)}`, () => {
30+
expect([...map.keys()]).toStrictEqual(expectedKeys);
31+
});
32+
33+
/**
34+
* NOTE: This is apparently an expected behaviour...?
35+
*
36+
* @see {@link https://github.com/npm/cli/issues/1886}
37+
*/
38+
it.fails(`'dependencies' at the root is '${undefined}'`, () => {
39+
expect(map.get("dependencies")).toBeUndefined();
40+
});
41+
42+
// eslint-disable-next-line unicorn/prevent-abbreviations
43+
const expectedDevDependencyName = "eslint";
44+
45+
it(`'devDeependencies' at the root has '${expectedDevDependencyName}'`, () => {
46+
expect(map.get("devDependencies")).toHaveProperty(expectedDevDependencyName);
47+
});
48+
49+
it(`'peerDeependencies' at the root has '${undefined}'`, () => {
50+
expect(map.get("peerDependencies")).toBeUndefined();
51+
});
52+
53+
const expectedOptionalDependencyName = "husky";
54+
55+
it(`'optionalDeependencies' at the root has '${expectedOptionalDependencyName}'`, () => {
56+
expect(map.get("optionalDependencies")).toHaveProperty(expectedOptionalDependencyName);
57+
});
58+
});
59+
60+
describe(`getAllPackagesNames()`, () => {
61+
const packages = getPackagesNames();
62+
63+
it(returns(packages).on(`the current repository with all package names`), () => {
64+
expect(packages).toBeInstanceOf(Set);
65+
expect(packages).toContain("typescript");
66+
});
67+
});
68+
69+
describe(`hasPackage(name)`, () => {
70+
const nonExistingPackageName = "terminal-nerdss";
71+
72+
it(returns(false).on(`non-existing package name in any of dependencies`).sample(nonExistingPackageName), () => {
73+
expect(hasModule(nonExistingPackageName)).toBe(false);
74+
});
75+
76+
const packageName = "typescript";
77+
78+
it(returns(true).on(`existing package name in any of dependencies`).sample(packageName), () => {
79+
expect(hasModule(packageName)).toBe(true);
80+
});
81+
});
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { RuntimeError } from "@terminal-nerds/snippets-error";
2+
import { resolveModule } from "local-pkg";
3+
import { type NormalizedPackageJson, readPackageUpSync } from "read-pkg-up";
4+
import type { KebabCase } from "type-fest/source/kebab-case.js";
5+
6+
import { IN_BROWSER } from "../environment/environment.js";
7+
8+
/** @see {@link https://nodejs.org/api/modules.html#modules-commonjs-modules} CommonJS Module */
9+
export const IN_CJS = typeof globalThis.require === "function";
10+
11+
/** @see {@link https://nodejs.org/api/esm.html#modules-ecmascript-modules} ECMAScript Module */
12+
export const IN_ESM = !IN_CJS && Boolean(import.meta);
13+
14+
export function hasModule(name: string): boolean {
15+
if (IN_BROWSER) throw new RuntimeError(`You cannot check for module existence in the browser.`);
16+
17+
return Boolean(resolveModule(name));
18+
}
19+
20+
export type PackageName<T extends string = string> = KebabCase<T>;
21+
// TODO: Add typings for SemVer and other doable stuff, e.g. `workspace:*`
22+
export type PackageVersion = string;
23+
24+
export function readPackageJSON(): NormalizedPackageJson {
25+
if (IN_BROWSER) throw new RuntimeError(`You cannot read the package.json inside the browser.`);
26+
27+
const file = readPackageUpSync();
28+
29+
if (file) {
30+
return file.packageJson;
31+
} else {
32+
throw new Error('Cannot locate nearest "package.json" file!');
33+
}
34+
}
35+
36+
export type DepedencyType = "dependencies" | "devDependencies" | "optionalDependencies" | "peerDependencies";
37+
export type Depedencies = Partial<Record<PackageName, PackageVersion>>;
38+
39+
export function getDependenciesMap(): Map<DepedencyType, Depedencies | undefined> {
40+
const { dependencies, devDependencies, optionalDependencies, peerDependencies } = readPackageJSON();
41+
42+
return new Map(
43+
Object.entries({
44+
dependencies,
45+
devDependencies,
46+
optionalDependencies,
47+
peerDependencies,
48+
}) as Array<[DepedencyType, Depedencies]>,
49+
);
50+
}
51+
52+
/** TODO: Add targetting, for better debugging (required, dev, optional or peer) */
53+
export function getDepedencies(): Depedencies {
54+
const { dependencies, devDependencies, optionalDependencies, peerDependencies } = readPackageJSON();
55+
56+
return { ...dependencies, ...devDependencies, ...optionalDependencies, ...peerDependencies };
57+
}
58+
59+
export function getPackagesNames(): Set<PackageName> {
60+
return new Set(Object.keys(getDepedencies()));
61+
}
62+
63+
/** TODO: Add targetting, for better debugging (required, dev, optional or peer) */
64+
export function hasPackage(name: string): boolean {
65+
return getPackagesNames().has(name);
66+
}

0 commit comments

Comments
 (0)