@unisphere/nx 3.20.0 → 3.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/dist/generators/add-application/add-application.d.ts.map +1 -1
  2. package/dist/generators/add-application/add-application.js +37 -16
  3. package/dist/generators/add-application/schema.d.ts +1 -1
  4. package/dist/generators/add-application/schema.json +4 -0
  5. package/dist/generators/add-application/templates/assets-only/.babelrc +11 -0
  6. package/dist/generators/add-application/templates/assets-only/.env-template +6 -0
  7. package/dist/generators/add-application/templates/assets-only/.eslintrc.json +22 -0
  8. package/dist/generators/add-application/templates/assets-only/package.json.template +5 -0
  9. package/dist/generators/add-application/templates/assets-only/project.json.template +9 -0
  10. package/dist/generators/add-application/templates/assets-only/readme.md.template +13 -0
  11. package/dist/generators/add-application/templates/assets-only/src/assets/README.md +20 -0
  12. package/dist/generators/add-application/templates/assets-only/src/index.html.template +16 -0
  13. package/dist/generators/add-application/templates/assets-only/src/main.tsx.template +2 -0
  14. package/dist/generators/add-application/templates/assets-only/tsconfig.app.json +11 -0
  15. package/dist/generators/add-application/templates/assets-only/tsconfig.json +17 -0
  16. package/dist/generators/add-application/templates/assets-only/webpack.config.js.template +24 -0
  17. package/dist/generators/change-package-scope/change-package-scope.d.ts +23 -0
  18. package/dist/generators/change-package-scope/change-package-scope.d.ts.map +1 -0
  19. package/dist/generators/change-package-scope/change-package-scope.js +345 -0
  20. package/dist/generators/change-package-scope/schema.d.ts +4 -0
  21. package/dist/generators/change-package-scope/schema.json +24 -0
  22. package/dist/generators/change-package-scope/utils.d.ts +38 -0
  23. package/dist/generators/change-package-scope/utils.d.ts.map +1 -0
  24. package/dist/generators/change-package-scope/utils.js +116 -0
  25. package/dist/generators/remove/remove.d.ts.map +1 -1
  26. package/dist/generators/remove/remove.js +10 -0
  27. package/generators.json +5 -0
  28. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"add-application.d.ts","sourceRoot":"","sources":["../../../src/generators/add-application/add-application.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,IAAI,EAAgD,MAAM,YAAY,CAAC;AAE5G,OAAO,EAAE,6BAA6B,EAAE,MAAM,UAAU,CAAC;AAkHzD,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,6BAA6B,uBAgKvC;AAED,eAAe,uBAAuB,CAAC"}
1
+ {"version":3,"file":"add-application.d.ts","sourceRoot":"","sources":["../../../src/generators/add-application/add-application.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,IAAI,EAAgD,MAAM,YAAY,CAAC;AAE5G,OAAO,EAAE,6BAA6B,EAAE,MAAM,UAAU,CAAC;AAkHzD,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,6BAA6B,uBAoLvC;AAED,eAAe,uBAAuB,CAAC"}
@@ -79,19 +79,30 @@ async function addApplicationGenerator(tree, options) {
79
79
  throw new Error('iframe-with-post-messages serving type is not currently supported. Please choose a different serving type.');
80
80
  }
81
81
  const isPlayground = options.servingType === 'local-dev-playground';
82
+ const isAssetsOnly = options.servingType === 'assets-only';
82
83
  // Validate and read .unisphere configuration
83
84
  const unisphereConfig = (0, utils_1.validateUnisphereConfig)(tree);
84
- const promptResult = await promptForRuntimeName(unisphereConfig.runtimes, options.runtimeName || '', options.visualName || '', options.skipDynamicRuntimeVisualPrompt || false);
85
- const runtimeName = promptResult.runtimeName;
86
- const visualName = promptResult.visualName;
87
- if (!isPlayground && !options.htmlPageTitle) {
88
- options.htmlPageTitle = options.htmlPageTitle || await promptForHtmlPageTitle();
85
+ let runtimeName = '';
86
+ let visualName = '';
87
+ let selectedDependencies = [];
88
+ if (isAssetsOnly) {
89
+ // Assets-only apps don't need runtime, visual, or dependencies
90
+ options.htmlPageTitle = options.htmlPageTitle || options.name;
91
+ selectedDependencies = [];
89
92
  }
90
- if (options.dependencies === undefined) {
91
- options.dependencies = await promptForDependencies(isPlayground);
93
+ else {
94
+ const promptResult = await promptForRuntimeName(unisphereConfig.runtimes, options.runtimeName || '', options.visualName || '', options.skipDynamicRuntimeVisualPrompt || false);
95
+ runtimeName = promptResult.runtimeName;
96
+ visualName = promptResult.visualName;
97
+ if (!isPlayground && !options.htmlPageTitle) {
98
+ options.htmlPageTitle = options.htmlPageTitle || await promptForHtmlPageTitle();
99
+ }
100
+ if (options.dependencies === undefined) {
101
+ options.dependencies = await promptForDependencies(isPlayground);
102
+ }
103
+ const baseDependencies = ['react'];
104
+ selectedDependencies = [...baseDependencies, ...(options.dependencies || [])];
92
105
  }
93
- const baseDependencies = ['react'];
94
- const selectedDependencies = [...baseDependencies, ...(options.dependencies || [])];
95
106
  // Validate runtime exists if runtimeName is provided
96
107
  if (runtimeName) {
97
108
  if (!(0, utils_1.checkIfRuntimeExists)(tree, runtimeName)) {
@@ -132,7 +143,7 @@ async function addApplicationGenerator(tree, options) {
132
143
  'Please choose a different application name or remove the existing application first.');
133
144
  }
134
145
  // Choose template based on serving type
135
- const templatePath = 'templates/default';
146
+ const templatePath = isAssetsOnly ? 'templates/assets-only' : 'templates/default';
136
147
  // Generate files from templates
137
148
  (0, devkit_1.generateFiles)(tree, path.join(__dirname, templatePath), projectRoot, templateVariables);
138
149
  // For local-dev-playground apps, generate setup-local-runtimes.ts
@@ -176,14 +187,19 @@ async function addApplicationGenerator(tree, options) {
176
187
  const applicationPathValue = `${projectRoot}/src/index.ts`;
177
188
  (0, utils_1.updateTsConfigPaths)(tree, applicationPathKey, applicationPathValue);
178
189
  const scriptName = `serve:${applicationNameAsLowerDashCase}`;
179
- const scriptCommand = runtimeName ?
180
- `concurrently "npx unisphere runtime serve ${runtimeName} --port 8400" "npx unisphere application serve ${applicationNameAsLowerDashCase} --port 4002"`
181
- : `npx unisphere application serve ${applicationNameAsLowerDashCase} --port 4002`;
182
- (0, utils_1.addScriptToRootPackageJson)(tree, scriptName, scriptCommand);
190
+ if (!isAssetsOnly) {
191
+ const scriptCommand = runtimeName ?
192
+ `concurrently "npx unisphere runtime serve ${runtimeName} --port 8400" "npx unisphere application serve ${applicationNameAsLowerDashCase} --port 4002"`
193
+ : `npx unisphere application serve ${applicationNameAsLowerDashCase} --port 4002`;
194
+ (0, utils_1.addScriptToRootPackageJson)(tree, scriptName, scriptCommand);
195
+ }
183
196
  await (0, devkit_1.formatFiles)(tree);
184
197
  // Return a function that will be executed after all file operations are complete
198
+ const hasExtraDependencies = selectedDependencies.length > 0;
185
199
  return () => {
186
- (0, devkit_1.installPackagesTask)(tree, true);
200
+ if (hasExtraDependencies) {
201
+ (0, devkit_1.installPackagesTask)(tree, true);
202
+ }
187
203
  devkit_1.logger.info('');
188
204
  devkit_1.logger.info('✅ Application generated successfully!');
189
205
  devkit_1.logger.info('');
@@ -191,7 +207,12 @@ async function addApplicationGenerator(tree, options) {
191
207
  devkit_1.logger.info(`🔧 Serving Type: ${options.servingType}`);
192
208
  devkit_1.logger.info(`📁 Location: ${projectRoot}`);
193
209
  devkit_1.logger.info('');
194
- devkit_1.logger.info(`📜 To run the application locally: npm run ${scriptName}`);
210
+ if (isAssetsOnly) {
211
+ devkit_1.logger.info(`📂 Place your assets in: ${projectRoot}/src/assets/`);
212
+ }
213
+ else {
214
+ devkit_1.logger.info(`📜 To run the application locally: npm run ${scriptName}`);
215
+ }
195
216
  devkit_1.logger.info('');
196
217
  };
197
218
  }
@@ -1,7 +1,7 @@
1
1
  export interface AddApplicationGeneratorSchema {
2
2
  name: string;
3
3
  htmlPageTitle?: string;
4
- servingType: 'local-dev-playground' | 'self-hosted' | 'iframe-with-post-messages';
4
+ servingType: 'local-dev-playground' | 'self-hosted' | 'iframe-with-post-messages' | 'assets-only';
5
5
  runtimeName?: string;
6
6
  visualName?: string;
7
7
  skipDynamicRuntimeVisualPrompt?: boolean; // Internal option to skip the dynamic prompt for visual when runtime is selected
@@ -33,6 +33,10 @@
33
33
  {
34
34
  "value": "iframe-with-post-messages",
35
35
  "label": "Iframe + Post Messages (suitible to applications that gets required configuration from post messages)"
36
+ },
37
+ {
38
+ "value": "assets-only",
39
+ "label": "Assets Only (static assets served via CDN, no application logic)"
36
40
  }
37
41
  ]
38
42
  }
@@ -0,0 +1,11 @@
1
+ {
2
+ "presets": [
3
+ [
4
+ "@nx/react/babel",
5
+ {
6
+ "runtime": "automatic"
7
+ }
8
+ ]
9
+ ],
10
+ "plugins": []
11
+ }
@@ -0,0 +1,6 @@
1
+ # Environment variables template
2
+ # Copy this file to .env and fill in the values
3
+ # Do not commit .env to version control
4
+
5
+ # Example:
6
+ # API_URL=https://api.example.com
@@ -0,0 +1,22 @@
1
+ {
2
+ "extends": ["plugin:@nx/react", "../../../../.eslintrc.json"],
3
+ "ignorePatterns": [
4
+ "!**/*",
5
+ "**/vite.config.*.timestamp*",
6
+ "**/vitest.config.*.timestamp*"
7
+ ],
8
+ "overrides": [
9
+ {
10
+ "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
11
+ "rules": {}
12
+ },
13
+ {
14
+ "files": ["*.ts", "*.tsx"],
15
+ "rules": {}
16
+ },
17
+ {
18
+ "files": ["*.js", "*.jsx"],
19
+ "rules": {}
20
+ }
21
+ ]
22
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "unisphere-application-<%= applicationName__lowerDashCase %>",
3
+ "version": "1.0.0",
4
+ "dependencies": {}
5
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "unisphere-application-<%= applicationName__lowerDashCase %>",
3
+ "$schema": "../../../../node_modules/nx/schemas/project-schema.json",
4
+ "sourceRoot": "<%= projectRoot %>/src",
5
+ "projectType": "application",
6
+ "tags": [],
7
+ "// targets": "to see all targets run: nx show project unisphere-application-<%= applicationName__lowerDashCase %> --web",
8
+ "targets": {}
9
+ }
@@ -0,0 +1,13 @@
1
+ # <%= applicationName__pascalCase %> Application (Assets Only)
2
+
3
+ This is an assets-only application. Place your static assets in the `src/assets/` directory.
4
+
5
+ ## Assets URL
6
+
7
+ After publishing, assets are available at:
8
+
9
+ ```
10
+ https://unisphere.nvp1.ovp.kaltura.com/v1/static/applications/{experience name}/<%= applicationName__lowerDashCase %>/{version}/assets/
11
+ ```
12
+
13
+ ## Overview section
@@ -0,0 +1,20 @@
1
+ # Assets
2
+
3
+ Place your static assets in this directory. They are bundled automatically during the build process.
4
+
5
+ ## Accessing Assets
6
+
7
+ Releasing a package will automatically deploy it to nvp1. Assets can be consumed without activating the version using the following URL pattern:
8
+
9
+ ```
10
+ https://unisphere.nvp1.ovp.kaltura.com/v1/static/applications/{experience name}/{app name}/{version}/assets/
11
+ ```
12
+
13
+ For example:
14
+ ```
15
+ https://unisphere.nvp1.ovp.kaltura.com/v1/static/applications/vod-avatars/studio/1.1.0/assets/avatars/1X1/adam.png
16
+ ```
17
+
18
+ ## Caching
19
+
20
+ Caching is handled on the assets directly.
@@ -0,0 +1,16 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="utf-8" />
6
+ <title>
7
+ <%= htmlPageTitle %>
8
+ </title>
9
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
10
+ </head>
11
+
12
+ <body>
13
+ <div id="root"></div>
14
+ </body>
15
+
16
+ </html>
@@ -0,0 +1,2 @@
1
+ // Assets-only application — no application logic required.
2
+ // Place your static assets in the ./assets/ directory.
@@ -0,0 +1,11 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "../../../dist/out-tsc",
5
+ "types": [
6
+ "node"
7
+ ]
8
+ },
9
+ "exclude": [],
10
+ "include": ["src/**/*.ts", "src/**/*.tsx"]
11
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "jsx": "react-jsx",
4
+ "allowJs": false,
5
+ "esModuleInterop": false,
6
+ "allowSyntheticDefaultImports": true,
7
+ "strict": true
8
+ },
9
+ "files": [],
10
+ "include": [],
11
+ "references": [
12
+ {
13
+ "path": "./tsconfig.app.json"
14
+ }
15
+ ],
16
+ "extends": "../../../../tsconfig.base.json"
17
+ }
@@ -0,0 +1,24 @@
1
+ const { NxAppWebpackPlugin } = require('@nx/webpack/app-plugin');
2
+ const { join } = require('path');
3
+
4
+ const baseHref = process.env.UNISPHERE_BASE_HREF || '/';
5
+
6
+ module.exports = {
7
+ output: {
8
+ path: join(__dirname, '../../../../dist/<%= projectRoot %>'),
9
+ publicPath: baseHref
10
+ },
11
+ plugins: [
12
+ new NxAppWebpackPlugin({
13
+ tsConfig: './tsconfig.app.json',
14
+ compiler: 'babel',
15
+ main: './src/main.tsx',
16
+ index: './src/index.html',
17
+ baseHref,
18
+ assets: [{ glob: '**/*', input: './src/assets', output: './assets', ignore: ['**/README.md'] }],
19
+ styles: [],
20
+ outputHashing: process.env['NODE_ENV'] === 'production' ? 'all' : 'none',
21
+ optimization: process.env['NODE_ENV'] === 'production',
22
+ }),
23
+ ],
24
+ };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Change Package Scope Generator
3
+ *
4
+ * Changes a package's npm scope by:
5
+ * 1. Validating the package exists and target scope is valid
6
+ * 2. Parsing the package.json name to extract current scope and package name
7
+ * 3. Using Nx's built-in move generator to handle:
8
+ * - Directory and file moves
9
+ * - Import statement updates across the workspace
10
+ * - TypeScript path mappings in tsconfig.base.json
11
+ * 4. Performing Unisphere-specific cleanup:
12
+ * - Updating .unisphere configuration
13
+ * - Ensuring package.json name matches conventions
14
+ * - Updating package-lock.json path keys
15
+ * - Updating vite.config.ts cache directory paths
16
+ * - Updating project.json lintFilePatterns and comments
17
+ * - Updating README.md with new package name
18
+ */
19
+ import { Tree } from '@nx/devkit';
20
+ import { ChangePackageScopeGeneratorSchema } from './schema';
21
+ export declare function changePackageScopeGenerator(tree: Tree, options: ChangePackageScopeGeneratorSchema): Promise<void>;
22
+ export default changePackageScopeGenerator;
23
+ //# sourceMappingURL=change-package-scope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"change-package-scope.d.ts","sourceRoot":"","sources":["../../../src/generators/change-package-scope/change-package-scope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EACL,IAAI,EAIL,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,iCAAiC,EAAE,MAAM,UAAU,CAAC;AAiW7D,wBAAsB,2BAA2B,CAC/C,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,iCAAiC,iBAoH3C;AAED,eAAe,2BAA2B,CAAC"}
@@ -0,0 +1,345 @@
1
+ "use strict";
2
+ /**
3
+ * Change Package Scope Generator
4
+ *
5
+ * Changes a package's npm scope by:
6
+ * 1. Validating the package exists and target scope is valid
7
+ * 2. Parsing the package.json name to extract current scope and package name
8
+ * 3. Using Nx's built-in move generator to handle:
9
+ * - Directory and file moves
10
+ * - Import statement updates across the workspace
11
+ * - TypeScript path mappings in tsconfig.base.json
12
+ * 4. Performing Unisphere-specific cleanup:
13
+ * - Updating .unisphere configuration
14
+ * - Ensuring package.json name matches conventions
15
+ * - Updating package-lock.json path keys
16
+ * - Updating vite.config.ts cache directory paths
17
+ * - Updating project.json lintFilePatterns and comments
18
+ * - Updating README.md with new package name
19
+ */
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.changePackageScopeGenerator = changePackageScopeGenerator;
22
+ const devkit_1 = require("@nx/devkit");
23
+ const generators_1 = require("@nx/workspace/generators");
24
+ const utils_1 = require("../utils");
25
+ const utils_2 = require("./utils");
26
+ /**
27
+ * Finds a package in .unisphere config by matching the package.json name.
28
+ * Extracts package information needed for scope change.
29
+ */
30
+ function findPackageInUnisphere(tree, packageJsonName, experienceName) {
31
+ const unisphereConfig = (0, devkit_1.readJson)(tree, '.unisphere');
32
+ if (!unisphereConfig.elements?.packages) {
33
+ throw new Error('No packages found in .unisphere configuration.\n' +
34
+ 'Cannot find package to change scope.');
35
+ }
36
+ // Parse the provided package.json name to get package identifier
37
+ const { packageName } = (0, utils_2.parsePackageJsonName)(packageJsonName, experienceName);
38
+ // Find the package by identifier in .unisphere
39
+ const packageConfig = unisphereConfig.elements.packages[packageName];
40
+ if (!packageConfig) {
41
+ throw new Error(`Package with identifier "${packageName}" not found in .unisphere configuration.\n` +
42
+ 'Available packages: ' +
43
+ Object.keys(unisphereConfig.elements.packages).join(', '));
44
+ }
45
+ const sourceRoot = packageConfig.sourceRoot;
46
+ if (!sourceRoot) {
47
+ throw new Error(`Package "${packageName}" exists but has no sourceRoot configured in .unisphere`);
48
+ }
49
+ // Verify the directory exists
50
+ if (!tree.exists(sourceRoot)) {
51
+ throw new Error(`Package directory not found at ${sourceRoot}.\n` +
52
+ 'The .unisphere configuration references a package that does not exist.');
53
+ }
54
+ // Read the package.json to verify the name matches
55
+ const packageJsonPath = `${sourceRoot}/package.json`;
56
+ if (!tree.exists(packageJsonPath)) {
57
+ throw new Error(`package.json not found at ${packageJsonPath}.\n` +
58
+ 'The package directory exists but is missing package.json.');
59
+ }
60
+ const packageJson = (0, devkit_1.readJson)(tree, packageJsonPath);
61
+ if (packageJson.name !== packageJsonName) {
62
+ throw new Error(`Package name mismatch!\n` +
63
+ `Expected: ${packageJsonName}\n` +
64
+ `Found in package.json: ${packageJson.name}\n` +
65
+ `Location: ${packageJsonPath}`);
66
+ }
67
+ const subdirectory = (0, utils_1.extractSubdirectoryFromSourceRoot)(sourceRoot);
68
+ const currentScope = (0, utils_2.extractScopeFromSubdirectory)(subdirectory);
69
+ return {
70
+ packageIdentifier: packageName,
71
+ packageJsonName,
72
+ currentScope,
73
+ sourceRoot,
74
+ subdirectory,
75
+ };
76
+ }
77
+ /**
78
+ * Validates that the target scope is different from current scope.
79
+ */
80
+ function validateDifferentScope(currentScope, newScope) {
81
+ if (currentScope === newScope) {
82
+ throw new Error(`Package is already in the target scope "${newScope}".\n` +
83
+ 'No changes needed.');
84
+ }
85
+ }
86
+ /**
87
+ * Validates that the target subdirectory doesn't already have a package with this identifier.
88
+ */
89
+ function validateNoCollision(tree, packageIdentifier, newSubdirectory) {
90
+ // Check if directory exists on filesystem
91
+ const newPath = `unisphere/packages/${newSubdirectory}/${packageIdentifier}`;
92
+ if (tree.exists(newPath)) {
93
+ throw new Error(`Directory already exists at ${newPath}.\n` +
94
+ 'Please choose a different scope or remove the existing directory first.');
95
+ }
96
+ }
97
+ /**
98
+ * Validates special package restrictions (e.g., types package scope rules).
99
+ */
100
+ function validateSpecialPackageRestrictions(packageIdentifier, newScope) {
101
+ if (packageIdentifier === 'types') {
102
+ const allowedScopes = ['@unisphere', '@kaltura-corp'];
103
+ if (!allowedScopes.includes(newScope)) {
104
+ throw new Error(`The "types" package can only exist in @unisphere or @kaltura-corp scopes.\n` +
105
+ `You attempted to move it to ${newScope}.\n` +
106
+ `Allowed scopes: ${allowedScopes.join(', ')}`);
107
+ }
108
+ }
109
+ }
110
+ /**
111
+ * Update .unisphere configuration with the new sourceRoot.
112
+ */
113
+ function updateUnisphereConfiguration(tree, packageIdentifier, newSourceRoot) {
114
+ const unisphereConfig = (0, devkit_1.readJson)(tree, '.unisphere');
115
+ const oldPackageConfig = unisphereConfig.elements.packages[packageIdentifier];
116
+ unisphereConfig.elements.packages[packageIdentifier] = {
117
+ ...oldPackageConfig,
118
+ sourceRoot: newSourceRoot,
119
+ };
120
+ (0, devkit_1.writeJson)(tree, '.unisphere', unisphereConfig);
121
+ devkit_1.logger.info(`✅ Updated .unisphere configuration`);
122
+ }
123
+ /**
124
+ * Update package.json name to ensure it matches Unisphere naming conventions
125
+ * Nx move generator sets the package name, but we verify it matches our conventions
126
+ */
127
+ function updatePackageJson(tree, packagePath, newPackageJsonName) {
128
+ const packageJsonPath = `${packagePath}/package.json`;
129
+ if (!tree.exists(packageJsonPath)) {
130
+ devkit_1.logger.warn(`⚠️ package.json not found at ${packageJsonPath}`);
131
+ return;
132
+ }
133
+ const packageJson = (0, devkit_1.readJson)(tree, packageJsonPath);
134
+ // Only update if Nx didn't set it to our expected name
135
+ if (packageJson.name !== newPackageJsonName) {
136
+ packageJson.name = newPackageJsonName;
137
+ (0, devkit_1.writeJson)(tree, packageJsonPath, packageJson);
138
+ devkit_1.logger.info(`✅ Updated package.json name to "${newPackageJsonName}"`);
139
+ }
140
+ }
141
+ /**
142
+ * Update package-lock.json to rename the package path keys
143
+ * Nx's move generator doesn't handle package-lock.json updates
144
+ */
145
+ function updatePackageLock(tree, oldPath, newPath) {
146
+ const packageLockPath = 'package-lock.json';
147
+ if (!tree.exists(packageLockPath)) {
148
+ return;
149
+ }
150
+ const packageLock = (0, devkit_1.readJson)(tree, packageLockPath);
151
+ let updated = false;
152
+ if (packageLock.packages) {
153
+ const packagesEntries = Object.entries(packageLock.packages);
154
+ const newPackages = {};
155
+ for (const [key, value] of packagesEntries) {
156
+ // Check if this key is exactly the old package path
157
+ if (key === oldPath) {
158
+ newPackages[newPath] = value;
159
+ updated = true;
160
+ }
161
+ else {
162
+ newPackages[key] = value;
163
+ }
164
+ }
165
+ packageLock.packages = newPackages;
166
+ }
167
+ // Update resolved paths if they exist
168
+ if (packageLock.packages) {
169
+ for (const [, value] of Object.entries(packageLock.packages)) {
170
+ if (value && typeof value === 'object' && 'resolved' in value) {
171
+ const pkg = value;
172
+ if (pkg.resolved === oldPath) {
173
+ pkg.resolved = newPath;
174
+ updated = true;
175
+ }
176
+ }
177
+ }
178
+ }
179
+ if (updated) {
180
+ (0, devkit_1.writeJson)(tree, packageLockPath, packageLock);
181
+ devkit_1.logger.info(`✅ Updated package-lock.json path keys`);
182
+ }
183
+ }
184
+ /**
185
+ * Update vite.config.ts to rename cache directory paths
186
+ * Nx's move generator doesn't update arbitrary string literals in code files
187
+ */
188
+ function updateViteConfig(tree, newPath, packageIdentifier) {
189
+ const viteConfigPath = `${newPath}/vite.config.ts`;
190
+ if (!tree.exists(viteConfigPath)) {
191
+ return;
192
+ }
193
+ let content = tree.read(viteConfigPath, 'utf-8');
194
+ if (!content) {
195
+ return;
196
+ }
197
+ const pattern = `package-${packageIdentifier}`;
198
+ // The cache directory already uses the package identifier, so we just verify it's there
199
+ // No changes needed for scope change
200
+ if (content.includes(pattern)) {
201
+ devkit_1.logger.info(`✅ Verified vite.config.ts cache directory`);
202
+ }
203
+ }
204
+ /**
205
+ * Update project.json to fix lintFilePatterns and comments
206
+ * Nx's move generator updates root/sourceRoot but not arbitrary strings in options
207
+ */
208
+ function updateProjectJson(tree, newPath, oldPath, nxProjectName) {
209
+ const projectJsonPath = `${newPath}/project.json`;
210
+ if (!tree.exists(projectJsonPath)) {
211
+ return;
212
+ }
213
+ let content = tree.read(projectJsonPath, 'utf-8');
214
+ if (!content) {
215
+ return;
216
+ }
217
+ let updated = false;
218
+ // Update lintFilePatterns that still reference the old path
219
+ if (content.includes(oldPath)) {
220
+ content = content.replace(new RegExp(escapeRegExp(oldPath), 'g'), newPath);
221
+ updated = true;
222
+ }
223
+ if (updated) {
224
+ tree.write(projectJsonPath, content);
225
+ devkit_1.logger.info(`✅ Updated project.json paths and comments`);
226
+ }
227
+ }
228
+ /**
229
+ * Update README.md to reflect the new package name
230
+ * Nx's move generator doesn't update documentation files
231
+ */
232
+ function updateReadme(tree, newPath, oldPackageJsonName, newPackageJsonName) {
233
+ const readmePath = `${newPath}/README.md`;
234
+ if (!tree.exists(readmePath)) {
235
+ return;
236
+ }
237
+ let content = tree.read(readmePath, 'utf-8');
238
+ if (!content) {
239
+ return;
240
+ }
241
+ let updated = false;
242
+ // Replace old package.json name with new package.json name
243
+ if (content.includes(oldPackageJsonName)) {
244
+ content = content.replace(new RegExp(escapeRegExp(oldPackageJsonName), 'g'), newPackageJsonName);
245
+ updated = true;
246
+ }
247
+ if (updated) {
248
+ tree.write(readmePath, content);
249
+ devkit_1.logger.info(`✅ Updated README.md`);
250
+ }
251
+ }
252
+ /**
253
+ * Escape special regex characters in a string
254
+ */
255
+ function escapeRegExp(string) {
256
+ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
257
+ }
258
+ async function changePackageScopeGenerator(tree, options) {
259
+ devkit_1.logger.info('');
260
+ devkit_1.logger.info('🔄 Starting package scope change...');
261
+ devkit_1.logger.info('');
262
+ // Validate .unisphere exists and get experience name
263
+ const unisphereConfig = (0, utils_1.validateUnisphereConfig)(tree);
264
+ const experienceName = unisphereConfig.name;
265
+ // Convert scope from schema format (without @) to internal format (with @)
266
+ // Schema provides: "kaltura-sdk", we need: "@kaltura-sdk"
267
+ const newScope = `@${options.newScope}`;
268
+ // Find package in .unisphere and validate it exists
269
+ const packageInfo = findPackageInUnisphere(tree, options.packageJsonName, experienceName);
270
+ devkit_1.logger.info(`📦 Package: ${packageInfo.packageIdentifier}`);
271
+ devkit_1.logger.info(`🏷️ Current: ${packageInfo.packageJsonName} (${packageInfo.subdirectory || 'root'}/)`);
272
+ // Validate target scope is different from current
273
+ validateDifferentScope(packageInfo.currentScope, newScope);
274
+ // Calculate new subdirectory and paths
275
+ const newSubdirectory = (0, utils_2.scopeToSubdirectory)(newScope);
276
+ // Validate no collision in target subdirectory
277
+ validateNoCollision(tree, packageInfo.packageIdentifier, newSubdirectory);
278
+ // Validate special package restrictions
279
+ validateSpecialPackageRestrictions(packageInfo.packageIdentifier, newScope);
280
+ // Calculate new package.json name
281
+ const newPackageJsonName = (0, utils_2.calculateNewPackageJsonName)(packageInfo.packageIdentifier, experienceName, newScope);
282
+ // Calculate new paths
283
+ const newPath = `unisphere/packages/${newSubdirectory}/${packageInfo.packageIdentifier}`;
284
+ const nxProjectName = `unisphere-package-${packageInfo.packageIdentifier}`;
285
+ devkit_1.logger.info(`🏷️ Target: ${newPackageJsonName} (${newSubdirectory}/)`);
286
+ devkit_1.logger.info(`📁 Moving: ${packageInfo.sourceRoot} → ${newPath}`);
287
+ devkit_1.logger.info('');
288
+ // Run Nx's built-in move generator
289
+ devkit_1.logger.info('🔧 Running Nx move generator...');
290
+ try {
291
+ await (0, generators_1.moveGenerator)(tree, {
292
+ projectName: nxProjectName,
293
+ destination: newPath,
294
+ newProjectName: nxProjectName,
295
+ importPath: newPackageJsonName,
296
+ updateImportPath: true,
297
+ skipFormat: false,
298
+ });
299
+ devkit_1.logger.info(`✅ Nx moved project and updated imports`);
300
+ }
301
+ catch (error) {
302
+ devkit_1.logger.error(`❌ Failed to move project with Nx: ${error}`);
303
+ throw error;
304
+ }
305
+ devkit_1.logger.info('');
306
+ devkit_1.logger.info('🧹 Performing Unisphere-specific cleanup...');
307
+ // Update .unisphere configuration
308
+ updateUnisphereConfiguration(tree, packageInfo.packageIdentifier, newPath);
309
+ // Update package.json to ensure correct naming
310
+ updatePackageJson(tree, newPath, newPackageJsonName);
311
+ // Update package-lock.json
312
+ updatePackageLock(tree, packageInfo.sourceRoot, newPath);
313
+ // Update vite.config.ts
314
+ updateViteConfig(tree, newPath, packageInfo.packageIdentifier);
315
+ // Update project.json
316
+ updateProjectJson(tree, newPath, packageInfo.sourceRoot, nxProjectName);
317
+ // Update README.md
318
+ updateReadme(tree, newPath, packageInfo.packageJsonName, newPackageJsonName);
319
+ devkit_1.logger.info('');
320
+ devkit_1.logger.info('✅ Package scope changed successfully!');
321
+ devkit_1.logger.info('');
322
+ devkit_1.logger.info('📋 Summary:');
323
+ devkit_1.logger.info(` • Package: ${packageInfo.packageIdentifier}`);
324
+ devkit_1.logger.info(` • Old scope: ${packageInfo.currentScope || 'none'}`);
325
+ devkit_1.logger.info(` • New scope: ${newScope}`);
326
+ devkit_1.logger.info(` • Old location: ${packageInfo.sourceRoot}`);
327
+ devkit_1.logger.info(` • New location: ${newPath}`);
328
+ devkit_1.logger.info(` • Old package.json name: ${packageInfo.packageJsonName}`);
329
+ devkit_1.logger.info(` • New package.json name: ${newPackageJsonName}`);
330
+ devkit_1.logger.info('');
331
+ devkit_1.logger.info('⚠️ IMPORTANT - Consumers must update:');
332
+ devkit_1.logger.info(' • Update all import statements from old to new package name');
333
+ devkit_1.logger.info(' • Update package.json dependencies if they reference this package');
334
+ devkit_1.logger.info(' • Run npm install to update lock file');
335
+ devkit_1.logger.info('');
336
+ devkit_1.logger.info('📝 Next steps:');
337
+ devkit_1.logger.info(' 1. Review the changes: git status');
338
+ devkit_1.logger.info(' 2. Stage all changes: git add -A');
339
+ devkit_1.logger.info(' Git will detect the directory move as a rename (preserving history)');
340
+ devkit_1.logger.info(' 3. Run: npm install (to update package-lock.json)');
341
+ devkit_1.logger.info(' 4. Run: npm run build (to verify everything builds)');
342
+ devkit_1.logger.info(' 5. Commit the changes: git commit -m "Change package scope..."');
343
+ devkit_1.logger.info('');
344
+ }
345
+ exports.default = changePackageScopeGenerator;
@@ -0,0 +1,4 @@
1
+ export interface ChangePackageScopeGeneratorSchema {
2
+ packageJsonName: string;
3
+ newScope: 'unisphere' | 'kaltura-corp' | 'kaltura-sdk' | 'kaltura-apps' | 'kaltura-ui' | 'local';
4
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "$schema": "https://json-schema.org/schema",
3
+ "$id": "ChangePackageScope",
4
+ "title": "Change Package Scope Generator",
5
+ "type": "object",
6
+ "properties": {
7
+ "packageJsonName": {
8
+ "type": "string",
9
+ "description": "The full package.json name (e.g., @kaltura-corp/unisphere-rtc-avatar)",
10
+ "pattern": "^@[a-z0-9-]+/[a-z0-9-]+$",
11
+ "x-prompt": "What is the full package.json name? (from the package's package.json)"
12
+ },
13
+ "newScope": {
14
+ "type": "string",
15
+ "description": "The target scope for the package",
16
+ "enum": ["unisphere", "kaltura-corp", "kaltura-sdk", "kaltura-apps", "kaltura-ui", "local"],
17
+ "x-prompt": "What scope should the package move to?"
18
+ }
19
+ },
20
+ "required": [
21
+ "packageJsonName",
22
+ "newScope"
23
+ ]
24
+ }
@@ -0,0 +1,38 @@
1
+ export interface ParsedPackageName {
2
+ scope: string;
3
+ packageName: string;
4
+ }
5
+ /**
6
+ * Parses a full package.json name to extract scope and package name.
7
+ * Handles the unisphere- prefix rules and tolerates incorrect prefixes.
8
+ *
9
+ * @param packageJsonName - Full package name (e.g., "@kaltura-corp/unisphere-rtc-avatar")
10
+ * @param experienceName - Experience name from .unisphere config (e.g., "rtc")
11
+ * @returns Parsed scope and package name
12
+ */
13
+ export declare function parsePackageJsonName(packageJsonName: string, experienceName: string): ParsedPackageName;
14
+ /**
15
+ * Calculates the new package.json name based on target scope and package name.
16
+ * Automatically handles unisphere- prefix rules.
17
+ *
18
+ * @param packageName - Package identifier (e.g., "avatar", "models")
19
+ * @param experienceName - Experience name from .unisphere config (e.g., "rtc")
20
+ * @param newScope - Target scope (e.g., "@kaltura-corp", "@unisphere")
21
+ * @returns New package.json name
22
+ */
23
+ export declare function calculateNewPackageJsonName(packageName: string, experienceName: string, newScope: string): string;
24
+ /**
25
+ * Extracts the scope from a subdirectory name.
26
+ *
27
+ * @param subdirectory - Subdirectory name (e.g., "kaltura-corp") or null for flat structure
28
+ * @returns Scope (e.g., "@kaltura-corp") or null
29
+ */
30
+ export declare function extractScopeFromSubdirectory(subdirectory: string | null): string | null;
31
+ /**
32
+ * Converts a scope to a subdirectory name.
33
+ *
34
+ * @param scope - Scope (e.g., "@kaltura-corp")
35
+ * @returns Subdirectory name (e.g., "kaltura-corp")
36
+ */
37
+ export declare function scopeToSubdirectory(scope: string): string;
38
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/generators/change-package-scope/utils.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,eAAe,EAAE,MAAM,EACvB,cAAc,EAAE,MAAM,GACrB,iBAAiB,CAkDnB;AAED;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CACzC,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,GACf,MAAM,CAiCR;AAED;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAKvF;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAWzD"}
@@ -0,0 +1,116 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parsePackageJsonName = parsePackageJsonName;
4
+ exports.calculateNewPackageJsonName = calculateNewPackageJsonName;
5
+ exports.extractScopeFromSubdirectory = extractScopeFromSubdirectory;
6
+ exports.scopeToSubdirectory = scopeToSubdirectory;
7
+ /**
8
+ * Parses a full package.json name to extract scope and package name.
9
+ * Handles the unisphere- prefix rules and tolerates incorrect prefixes.
10
+ *
11
+ * @param packageJsonName - Full package name (e.g., "@kaltura-corp/unisphere-rtc-avatar")
12
+ * @param experienceName - Experience name from .unisphere config (e.g., "rtc")
13
+ * @returns Parsed scope and package name
14
+ */
15
+ function parsePackageJsonName(packageJsonName, experienceName) {
16
+ if (!packageJsonName?.trim()) {
17
+ throw new Error('packageJsonName cannot be empty');
18
+ }
19
+ if (!experienceName?.trim()) {
20
+ throw new Error('experienceName cannot be empty');
21
+ }
22
+ // Validate format: must start with @scope/
23
+ const scopeMatch = packageJsonName.match(/^(@[^/]+)\//);
24
+ if (!scopeMatch) {
25
+ throw new Error(`Invalid package.json name format: "${packageJsonName}"\n` +
26
+ 'Expected format: @scope/package-name');
27
+ }
28
+ const scope = scopeMatch[1];
29
+ let nameAfterScope = packageJsonName.substring(scope.length + 1); // +1 for the '/'
30
+ // Remove unisphere- prefix if present (handle incorrect usage)
31
+ if (nameAfterScope.startsWith('unisphere-')) {
32
+ nameAfterScope = nameAfterScope.substring('unisphere-'.length);
33
+ }
34
+ // Remove experience prefix
35
+ if (nameAfterScope === experienceName) {
36
+ // Special case: package name is same as experience (e.g., "@unisphere/rtc")
37
+ return { scope, packageName: experienceName };
38
+ }
39
+ const expectedPrefix = `${experienceName}-`;
40
+ if (!nameAfterScope.startsWith(expectedPrefix)) {
41
+ throw new Error(`Package name "${packageJsonName}" does not follow expected format.\n` +
42
+ `Expected: ${scope}/${expectedPrefix}package-name or ${scope}/${experienceName}\n` +
43
+ `Found: ${packageJsonName}`);
44
+ }
45
+ const packageName = nameAfterScope.substring(expectedPrefix.length);
46
+ if (!packageName) {
47
+ throw new Error(`Package name "${packageJsonName}" results in empty package name.\n` +
48
+ `Expected: ${scope}/${expectedPrefix}<package-name>`);
49
+ }
50
+ return { scope, packageName };
51
+ }
52
+ /**
53
+ * Calculates the new package.json name based on target scope and package name.
54
+ * Automatically handles unisphere- prefix rules.
55
+ *
56
+ * @param packageName - Package identifier (e.g., "avatar", "models")
57
+ * @param experienceName - Experience name from .unisphere config (e.g., "rtc")
58
+ * @param newScope - Target scope (e.g., "@kaltura-corp", "@unisphere")
59
+ * @returns New package.json name
60
+ */
61
+ function calculateNewPackageJsonName(packageName, experienceName, newScope) {
62
+ if (!packageName?.trim()) {
63
+ throw new Error('packageName cannot be empty');
64
+ }
65
+ if (!experienceName?.trim()) {
66
+ throw new Error('experienceName cannot be empty');
67
+ }
68
+ if (!newScope?.trim()) {
69
+ throw new Error('newScope cannot be empty');
70
+ }
71
+ if (!newScope.startsWith('@')) {
72
+ throw new Error(`Invalid scope format: "${newScope}"\n` +
73
+ 'Expected format: @scope (e.g., @unisphere, @kaltura-corp)');
74
+ }
75
+ // Special case: package name equals experience name
76
+ if (packageName === experienceName) {
77
+ if (newScope === '@kaltura-corp') {
78
+ return `@kaltura-corp/unisphere-${experienceName}`;
79
+ }
80
+ return `${newScope}/${experienceName}`;
81
+ }
82
+ // Regular case: experience-package
83
+ const baseName = `${experienceName}-${packageName}`;
84
+ if (newScope === '@kaltura-corp') {
85
+ return `@kaltura-corp/unisphere-${baseName}`;
86
+ }
87
+ return `${newScope}/${baseName}`;
88
+ }
89
+ /**
90
+ * Extracts the scope from a subdirectory name.
91
+ *
92
+ * @param subdirectory - Subdirectory name (e.g., "kaltura-corp") or null for flat structure
93
+ * @returns Scope (e.g., "@kaltura-corp") or null
94
+ */
95
+ function extractScopeFromSubdirectory(subdirectory) {
96
+ if (!subdirectory?.trim()) {
97
+ return null;
98
+ }
99
+ return `@${subdirectory}`;
100
+ }
101
+ /**
102
+ * Converts a scope to a subdirectory name.
103
+ *
104
+ * @param scope - Scope (e.g., "@kaltura-corp")
105
+ * @returns Subdirectory name (e.g., "kaltura-corp")
106
+ */
107
+ function scopeToSubdirectory(scope) {
108
+ if (!scope?.trim()) {
109
+ throw new Error('scope cannot be empty');
110
+ }
111
+ if (!scope.startsWith('@')) {
112
+ throw new Error(`Invalid scope format: "${scope}"\n` +
113
+ 'Expected format: @scope (e.g., @unisphere, @kaltura-corp)');
114
+ }
115
+ return scope.substring(1);
116
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"remove.d.ts","sourceRoot":"","sources":["../../../src/generators/remove/remove.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EACL,IAAI,EAKL,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAoQjD,wBAAsB,eAAe,CACnC,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,qBAAqB,iBAqF/B;AAED,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"remove.d.ts","sourceRoot":"","sources":["../../../src/generators/remove/remove.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EACL,IAAI,EAKL,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAiRjD,wBAAsB,eAAe,CACnC,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,qBAAqB,iBAqF/B;AAED,eAAe,eAAe,CAAC"}
@@ -193,10 +193,20 @@ function removeFromPackageLock(tree, sourceRoot) {
193
193
  const packageLock = (0, devkit_1.readJson)(tree, packageLockPath);
194
194
  let updated = false;
195
195
  if (packageLock.packages) {
196
+ // Remove the source entry
196
197
  if (packageLock.packages[sourceRoot]) {
197
198
  delete packageLock.packages[sourceRoot];
198
199
  updated = true;
199
200
  }
201
+ // Remove the node_modules symlink entry that points to this sourceRoot
202
+ for (const key of Object.keys(packageLock.packages)) {
203
+ if (key.startsWith('node_modules/') &&
204
+ packageLock.packages[key].resolved === sourceRoot &&
205
+ packageLock.packages[key].link === true) {
206
+ delete packageLock.packages[key];
207
+ updated = true;
208
+ }
209
+ }
200
210
  }
201
211
  if (updated) {
202
212
  (0, devkit_1.writeJson)(tree, packageLockPath, packageLock);
package/generators.json CHANGED
@@ -30,6 +30,11 @@
30
30
  "schema": "./dist/generators/rename-package/schema.json",
31
31
  "description": "Rename a unisphere package and update all references"
32
32
  },
33
+ "change-package-scope": {
34
+ "factory": "./dist/generators/change-package-scope/change-package-scope",
35
+ "schema": "./dist/generators/change-package-scope/schema.json",
36
+ "description": "Change a package's npm scope by moving it between subdirectories"
37
+ },
33
38
  "remove": {
34
39
  "factory": "./dist/generators/remove/remove",
35
40
  "schema": "./dist/generators/remove/schema.json",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unisphere/nx",
3
- "version": "3.20.0",
3
+ "version": "3.22.0",
4
4
  "private": false,
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",