Skip to content

Commit 5016ed2

Browse files
authored
Add package name stripping the "scope" part from library names (#277)
* Rename PathSuffixChoice to LibraryNamingChoice and add packageName option defaulting to strip * Add changeset * Add fixes from code review
1 parent 6996e73 commit 5016ed2

File tree

7 files changed

+188
-38
lines changed

7 files changed

+188
-38
lines changed

.changeset/orange-bananas-obey.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"react-native-node-api": minor
3+
---
4+
5+
Scope is now stripped from package names when renaming libraries while linking

apps/test-app/babel.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
module.exports = {
22
presets: ["module:@react-native/babel-preset"],
3-
// plugins: [['module:react-native-node-api/babel-plugin', { stripPathSuffix: true }]],
3+
// plugins: [['module:react-native-node-api/babel-plugin', { packageName: "strip", pathSuffix: "strip" }]],
44
plugins: ["module:react-native-node-api/babel-plugin"],
55
};

packages/host/src/node/babel-plugin/plugin.ts

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,22 @@ import {
99
isNodeApiModule,
1010
findNodeAddonForBindings,
1111
NamingStrategy,
12-
PathSuffixChoice,
13-
assertPathSuffix,
12+
LibraryNamingChoice,
13+
assertLibraryNamingChoice,
1414
} from "../path-utils";
1515

1616
export type PluginOptions = {
17+
/**
18+
* Controls how the package name is transformed into a library name.
19+
* The transformation is needed to disambiguate and avoid conflicts between addons with the same name (but in different sub-paths or packages).
20+
*
21+
* As an example, if the package name is `@my-org/my-pkg` and the path of the addon within the package is `build/Release/my-addon.node` (and `pathSuffix` is set to `"strip"`):
22+
* - `"omit"`: Only the path within the package is used and the library name will be `my-addon`.
23+
* - `"strip"`: Scope / org gets stripped and the library name will be `my-pkg--my-addon`.
24+
* - `"keep"`: The org and name is kept and the library name will be `my-org--my-pkg--my-addon`.
25+
*/
26+
packageName?: LibraryNamingChoice;
27+
1728
/**
1829
* Controls how the path of the addon inside a package is transformed into a library name.
1930
* The transformation is needed to disambiguate and avoid conflicts between addons with the same name (but in different sub-paths or packages).
@@ -23,13 +34,16 @@ export type PluginOptions = {
2334
* - `"strip"` (default): Path gets stripped to its basename and the library name will be `my-pkg--my-addon`.
2435
* - `"keep"`: The full path is kept and the library name will be `my-pkg--build-Release-my-addon`.
2536
*/
26-
pathSuffix?: PathSuffixChoice;
37+
pathSuffix?: LibraryNamingChoice;
2738
};
2839

2940
function assertOptions(opts: unknown): asserts opts is PluginOptions {
3041
assert(typeof opts === "object" && opts !== null, "Expected an object");
3142
if ("pathSuffix" in opts) {
32-
assertPathSuffix(opts.pathSuffix);
43+
assertLibraryNamingChoice(opts.pathSuffix);
44+
}
45+
if ("packageName" in opts) {
46+
assertLibraryNamingChoice(opts.packageName);
3347
}
3448
}
3549

@@ -57,7 +71,7 @@ export function plugin(): PluginObj {
5771
visitor: {
5872
CallExpression(p) {
5973
assertOptions(this.opts);
60-
const { pathSuffix = "strip" } = this.opts;
74+
const { pathSuffix = "strip", packageName = "strip" } = this.opts;
6175
if (typeof this.filename !== "string") {
6276
// This transformation only works when the filename is known
6377
return;
@@ -80,6 +94,7 @@ export function plugin(): PluginObj {
8094
const resolvedPath = findNodeAddonForBindings(id, from);
8195
if (typeof resolvedPath === "string") {
8296
replaceWithRequireNodeAddon(p.parentPath, resolvedPath, {
97+
packageName,
8398
pathSuffix,
8499
});
85100
}
@@ -89,7 +104,10 @@ export function plugin(): PluginObj {
89104
isNodeApiModule(path.join(from, id))
90105
) {
91106
const relativePath = path.join(from, id);
92-
replaceWithRequireNodeAddon(p, relativePath, { pathSuffix });
107+
replaceWithRequireNodeAddon(p, relativePath, {
108+
packageName,
109+
pathSuffix,
110+
});
93111
}
94112
}
95113
},
Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,28 @@
11
import { Option } from "@react-native-node-api/cli-utils";
22

3-
import { assertPathSuffix, PATH_SUFFIX_CHOICES } from "../path-utils";
3+
import {
4+
assertLibraryNamingChoice,
5+
LIBRARY_NAMING_CHOICES,
6+
} from "../path-utils";
47

5-
const { NODE_API_PATH_SUFFIX } = process.env;
8+
const { NODE_API_PACKAGE_NAME, NODE_API_PATH_SUFFIX } = process.env;
9+
if (typeof NODE_API_PACKAGE_NAME === "string") {
10+
assertLibraryNamingChoice(NODE_API_PACKAGE_NAME);
11+
}
612
if (typeof NODE_API_PATH_SUFFIX === "string") {
7-
assertPathSuffix(NODE_API_PATH_SUFFIX);
13+
assertLibraryNamingChoice(NODE_API_PATH_SUFFIX);
814
}
915

16+
export const packageNameOption = new Option(
17+
"--package-name <strategy>",
18+
"Controls how the package name is transformed into a library name",
19+
)
20+
.choices(LIBRARY_NAMING_CHOICES)
21+
.default(NODE_API_PACKAGE_NAME || "strip");
22+
1023
export const pathSuffixOption = new Option(
1124
"--path-suffix <strategy>",
1225
"Controls how the path of the addon inside a package is transformed into a library name",
1326
)
14-
.choices(PATH_SUFFIX_CHOICES)
27+
.choices(LIBRARY_NAMING_CHOICES)
1528
.default(NODE_API_PATH_SUFFIX || "strip");

packages/host/src/node/cli/program.ts

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import {
2323
} from "../path-utils";
2424

2525
import { command as vendorHermes } from "./hermes";
26-
import { pathSuffixOption } from "./options";
26+
import { packageNameOption, pathSuffixOption } from "./options";
2727
import { linkModules, pruneLinkedModules, ModuleLinker } from "./link-modules";
2828
import { linkXcframework } from "./apple";
2929
import { linkAndroidDir } from "./android";
@@ -70,10 +70,14 @@ program
7070
)
7171
.option("--android", "Link Android modules")
7272
.option("--apple", "Link Apple modules")
73+
.addOption(packageNameOption)
7374
.addOption(pathSuffixOption)
7475
.action(
7576
wrapAction(
76-
async (pathArg, { force, prune, pathSuffix, android, apple }) => {
77+
async (
78+
pathArg,
79+
{ force, prune, pathSuffix, android, apple, packageName },
80+
) => {
7781
console.log("Auto-linking Node-API modules from", chalk.dim(pathArg));
7882
const platforms: PlatformName[] = [];
7983
if (android) {
@@ -101,7 +105,7 @@ program
101105
platform,
102106
fromPath: path.resolve(pathArg),
103107
incremental: !force,
104-
naming: { pathSuffix },
108+
naming: { packageName, pathSuffix },
105109
linker: getLinker(platform),
106110
}),
107111
{
@@ -173,9 +177,10 @@ program
173177
.description("Lists Node-API modules")
174178
.argument("[from-path]", "Some path inside the app package", process.cwd())
175179
.option("--json", "Output as JSON", false)
180+
.addOption(packageNameOption)
176181
.addOption(pathSuffixOption)
177182
.action(
178-
wrapAction(async (fromArg, { json, pathSuffix }) => {
183+
wrapAction(async (fromArg, { json, pathSuffix, packageName }) => {
179184
const rootPath = path.resolve(fromArg);
180185
const dependencies = await findNodeApiModulePathsByDependency({
181186
fromPath: rootPath,
@@ -210,7 +215,7 @@ program
210215
);
211216
logModulePaths(
212217
dependency.modulePaths.map((p) => path.join(dependency.path, p)),
213-
{ pathSuffix },
218+
{ packageName, pathSuffix },
214219
);
215220
}
216221
}
@@ -222,21 +227,22 @@ program
222227
.description(
223228
"Utility to print, module path, the hash of a single Android library",
224229
)
230+
.addOption(packageNameOption)
225231
.addOption(pathSuffixOption)
226232
.action(
227-
wrapAction((pathInput, { pathSuffix }) => {
233+
wrapAction((pathInput, { pathSuffix, packageName }) => {
228234
const resolvedModulePath = path.resolve(pathInput);
229235
const normalizedModulePath = normalizeModulePath(resolvedModulePath);
230-
const { packageName, relativePath } =
231-
determineModuleContext(resolvedModulePath);
236+
const context = determineModuleContext(resolvedModulePath);
232237
const libraryName = getLibraryName(resolvedModulePath, {
238+
packageName,
233239
pathSuffix,
234240
});
235241
console.log({
236242
resolvedModulePath,
237243
normalizedModulePath,
238-
packageName,
239-
relativePath,
244+
packageName: context.packageName,
245+
relativePath: context.relativePath,
240246
libraryName,
241247
});
242248
}),

packages/host/src/node/path-utils.test.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,13 +208,15 @@ describe("getLibraryName", () => {
208208
});
209209
assert.equal(
210210
getLibraryName(path.join(tempDirectoryPath, "addon"), {
211+
packageName: "keep",
211212
pathSuffix: "keep",
212213
}),
213214
"my-package--addon",
214215
);
215216

216217
assert.equal(
217218
getLibraryName(path.join(tempDirectoryPath, "sub-directory/addon"), {
219+
packageName: "keep",
218220
pathSuffix: "keep",
219221
}),
220222
"my-package--sub-directory-addon",
@@ -230,13 +232,15 @@ describe("getLibraryName", () => {
230232
});
231233
assert.equal(
232234
getLibraryName(path.join(tempDirectoryPath, "addon"), {
235+
packageName: "keep",
233236
pathSuffix: "strip",
234237
}),
235238
"my-package--addon",
236239
);
237240

238241
assert.equal(
239242
getLibraryName(path.join(tempDirectoryPath, "sub-directory", "addon"), {
243+
packageName: "keep",
240244
pathSuffix: "strip",
241245
}),
242246
"my-package--addon",
@@ -252,18 +256,62 @@ describe("getLibraryName", () => {
252256
});
253257
assert.equal(
254258
getLibraryName(path.join(tempDirectoryPath, "addon"), {
259+
packageName: "keep",
255260
pathSuffix: "omit",
256261
}),
257262
"my-package",
258263
);
259264

260265
assert.equal(
261266
getLibraryName(path.join(tempDirectoryPath, "sub-directory", "addon"), {
267+
packageName: "keep",
262268
pathSuffix: "omit",
263269
}),
264270
"my-package",
265271
);
266272
});
273+
274+
it("keeps and escapes scope from package name", (context) => {
275+
const tempDirectoryPath = setupTempDirectory(context, {
276+
"package.json": `{ "name": "@my-org/my-package" }`,
277+
"addon.apple.node/addon.node": "// This is supposed to be a binary file",
278+
});
279+
assert.equal(
280+
getLibraryName(path.join(tempDirectoryPath, "addon"), {
281+
packageName: "keep",
282+
pathSuffix: "strip",
283+
}),
284+
"my-org__my-package--addon",
285+
);
286+
});
287+
288+
it("strips scope from package name", (context) => {
289+
const tempDirectoryPath = setupTempDirectory(context, {
290+
"package.json": `{ "name": "@my-org/my-package" }`,
291+
"addon.apple.node/addon.node": "// This is supposed to be a binary file",
292+
});
293+
assert.equal(
294+
getLibraryName(path.join(tempDirectoryPath, "addon"), {
295+
packageName: "strip",
296+
pathSuffix: "strip",
297+
}),
298+
"my-package--addon",
299+
);
300+
});
301+
302+
it("omits scope from package name", (context) => {
303+
const tempDirectoryPath = setupTempDirectory(context, {
304+
"package.json": `{ "name": "@my-org/my-package" }`,
305+
"addon.apple.node/addon.node": "// This is supposed to be a binary file",
306+
});
307+
assert.equal(
308+
getLibraryName(path.join(tempDirectoryPath, "addon"), {
309+
packageName: "omit",
310+
pathSuffix: "strip",
311+
}),
312+
"addon",
313+
);
314+
});
267315
});
268316

269317
describe("findPackageDependencyPaths", () => {

0 commit comments

Comments
 (0)