proteum 2.1.7 → 2.1.9

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.
@@ -22,8 +22,15 @@ const readServerTsconfigPaths = () => {
22
22
  return compilerOptions.paths || {};
23
23
  };
24
24
 
25
- const createCommandsTsconfigContent = () =>
26
- `${JSON.stringify(
25
+ const getCommandsTsconfigFilepath = () => path.join(app.paths.root, 'commands', 'tsconfig.json');
26
+
27
+ const getCommandsGlobalTypesPath = (commandsTsconfigFilepath: string) =>
28
+ cli.paths.relativeFrameworkPathFrom(commandsTsconfigFilepath, 'types', 'global');
29
+
30
+ const createCommandsTsconfigContent = () => {
31
+ const commandsTsconfigFilepath = getCommandsTsconfigFilepath();
32
+
33
+ return `${JSON.stringify(
27
34
  {
28
35
  extends: '../server/tsconfig.json',
29
36
  compilerOptions: {
@@ -35,12 +42,13 @@ const createCommandsTsconfigContent = () =>
35
42
  '@models/types': ['./.proteum/server/models.ts'],
36
43
  },
37
44
  },
38
- include: ['.', '../var/typings', '../node_modules/proteum/types/global', '../.proteum/server/commands.d.ts'],
45
+ include: ['.', '../var/typings', getCommandsGlobalTypesPath(commandsTsconfigFilepath), '../.proteum/server/commands.d.ts'],
39
46
  },
40
47
  null,
41
48
  4,
42
49
  )}
43
50
  `;
51
+ };
44
52
 
45
53
  const legacyCommandsTsconfigContent = `{
46
54
  "extends": "../server/tsconfig.json",
@@ -105,8 +113,15 @@ const isManagedCommandsTsconfig = (content: string) => {
105
113
  };
106
114
 
107
115
  if (parsed.extends !== '../server/tsconfig.json') return false;
108
- if (JSON.stringify(parsed.include || []) !== JSON.stringify(['.', '../var/typings', '../node_modules/proteum/types/global', '../.proteum/server/commands.d.ts']))
116
+ if (!Array.isArray(parsed.include) || parsed.include.length !== 4) return false;
117
+ if (parsed.include[0] !== '.' || parsed.include[1] !== '../var/typings') return false;
118
+ if (parsed.include[3] !== '../.proteum/server/commands.d.ts') return false;
119
+ if (
120
+ parsed.include[2] !== getCommandsGlobalTypesPath(getCommandsTsconfigFilepath()) &&
121
+ !parsed.include[2].includes('node_modules/proteum/types/global')
122
+ ) {
109
123
  return false;
124
+ }
110
125
 
111
126
  if (parsed.compilerOptions?.baseUrl !== undefined && parsed.compilerOptions.baseUrl !== '..') return false;
112
127
  if (parsed.compilerOptions?.rootDir !== undefined && parsed.compilerOptions.rootDir !== '..') return false;
@@ -119,7 +134,7 @@ const isManagedCommandsTsconfig = (content: string) => {
119
134
 
120
135
  const ensureCommandsTsconfig = () => {
121
136
  const commandsRoot = path.join(app.paths.root, 'commands');
122
- const commandsTsconfigFilepath = path.join(commandsRoot, 'tsconfig.json');
137
+ const commandsTsconfigFilepath = getCommandsTsconfigFilepath();
123
138
  const nextContent = createCommandsTsconfigContent();
124
139
 
125
140
  if (!fs.existsSync(commandsRoot)) return;
@@ -21,17 +21,18 @@ import type { App } from '../../app';
21
21
 
22
22
  const debug = false;
23
23
  const ssrScriptPattern = /\.ssr\.(ts|tsx)$/;
24
- const normalizedCoreRoot = cli.paths.core.root.replace(/\\/g, '/');
24
+ const normalizedCoreRoot = cli.paths.framework.activeRoot.replace(/\\/g, '/');
25
25
  const hmrClientEntry = path.join(cli.paths.core.root, 'client', 'dev', 'hmr.ts');
26
26
 
27
27
  const normalizeModulePath = (value?: string) => (value || '').replace(/\\/g, '/');
28
- const resolveFromAppOrCore = (app: App, request: string) =>
29
- require.resolve(request, { paths: [app.paths.root, cli.paths.core.root] });
28
+ const resolveFromAppOrCore = (_app: App, request: string) => cli.paths.resolveRequest(request);
30
29
  const rewriteFrameworkAliasTargets = (app: App, aliases: Record<string, string | string[]>) => {
31
- const installedCoreRoot = normalizeModulePath(path.join(app.paths.root, 'node_modules', 'proteum'));
32
- const activeCoreRoot = normalizeModulePath(cli.paths.core.root);
30
+ const installedCoreRoot = cli.paths.framework.installedRoot
31
+ ? normalizeModulePath(cli.paths.framework.installedRoot)
32
+ : undefined;
33
+ const activeCoreRoot = normalizeModulePath(cli.paths.framework.activeRoot);
33
34
 
34
- if (installedCoreRoot === activeCoreRoot) return aliases;
35
+ if (!installedCoreRoot || installedCoreRoot === activeCoreRoot) return aliases;
35
36
 
36
37
  const rewriteCandidate = (candidate: string) =>
37
38
  normalizeModulePath(candidate).startsWith(installedCoreRoot + '/')
@@ -76,8 +77,7 @@ export default function createCompiler(
76
77
  logVerbose(`Creating compiler for client (${mode}).`);
77
78
  const dev = mode === 'dev';
78
79
  const outputPath = app.outputPath(outputTarget);
79
- const installedCoreRoot = path.join(app.paths.root, 'node_modules', 'proteum');
80
- const frameworkRoots = [cli.paths.core.root, installedCoreRoot];
80
+ const frameworkRoots = cli.paths.getFrameworkRoots();
81
81
  const transpileModuleDirectories = app.transpileModuleDirectories;
82
82
 
83
83
  const commonConfig = createCommonConfig(app, 'client', mode, outputTarget);
@@ -93,7 +93,7 @@ export default function createCompiler(
93
93
  );*/
94
94
 
95
95
  // Convert tsconfig paths into bundler aliases.
96
- const { aliases } = app.aliases.client.forWebpack({ modulesPath: app.paths.root + '/node_modules' });
96
+ const { aliases } = app.aliases.client.forWebpack({ modulesPath: cli.paths.framework.appNodeModulesRoot });
97
97
  const resolvedAliases = rewriteFrameworkAliasTargets(app, aliases);
98
98
 
99
99
  // We're not supposed in any case to import server libs from client
@@ -1,6 +1,7 @@
1
1
  import fs from 'fs-extra';
2
2
  import path from 'path';
3
3
  import type { RspackPluginInstance } from '@rspack/core';
4
+ import { UsageError } from 'clipanion';
4
5
 
5
6
  import cli from '../..';
6
7
  import type { App } from '../../app';
@@ -8,8 +9,56 @@ import type { App } from '../../app';
8
9
  const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
9
10
 
10
11
  export type TBundleAnalysisReportPaths = { reportPath: string; statsPath: string };
12
+ export type TBundleAnalysisMode = 'server' | 'static';
13
+ type TBundleAnalysisServerUrlArgs = {
14
+ listenHost: string;
15
+ listenPort: number | 'auto';
16
+ boundAddress?: string | { address?: string; port?: number } | null;
17
+ };
18
+
19
+ const defaultAnalyzerHost = '127.0.0.1';
20
+ const defaultAnalyzerPort = 8888;
21
+ let latestClientBundleAnalysisServerUrl: string | undefined;
11
22
 
12
23
  export const isBundleAnalysisEnabled = () => cli.args.analyze === true;
24
+ export const isBundleAnalysisServerEnabled = () => cli.args.analyzeServe === true;
25
+ export const getBundleAnalysisMode = (): TBundleAnalysisMode => (isBundleAnalysisServerEnabled() ? 'server' : 'static');
26
+
27
+ const hasCliStringArg = (name: string) => typeof cli.args[name] === 'string' && (cli.args[name] as string).trim().length > 0;
28
+
29
+ export const hasBundleAnalysisServerOverrides = () => hasCliStringArg('analyzeHost') || hasCliStringArg('analyzePort');
30
+
31
+ export const getBundleAnalysisServerHost = () =>
32
+ hasCliStringArg('analyzeHost') ? String(cli.args.analyzeHost).trim() : defaultAnalyzerHost;
33
+
34
+ export const getBundleAnalysisServerPort = (): number | 'auto' => {
35
+ const rawPort = hasCliStringArg('analyzePort') ? String(cli.args.analyzePort).trim() : '';
36
+ if (!rawPort) return defaultAnalyzerPort;
37
+ if (rawPort === 'auto') return 'auto';
38
+
39
+ const parsedPort = Number.parseInt(rawPort, 10);
40
+ if (!Number.isInteger(parsedPort) || parsedPort < 1 || parsedPort > 65535) {
41
+ throw new UsageError(`Invalid analyzer port "${rawPort}". Use a number between 1 and 65535, or \`auto\`.`);
42
+ }
43
+
44
+ return parsedPort;
45
+ };
46
+
47
+ const createBundleAnalysisServerUrl = ({ listenHost, listenPort, boundAddress }: TBundleAnalysisServerUrlArgs) => {
48
+ const port =
49
+ typeof boundAddress === 'object' && boundAddress !== null && typeof boundAddress.port === 'number'
50
+ ? boundAddress.port
51
+ : listenPort;
52
+ const url = `http://${listenHost}:${port}`;
53
+ latestClientBundleAnalysisServerUrl = url;
54
+ return url;
55
+ };
56
+
57
+ export const consumeClientBundleAnalysisServerUrl = () => {
58
+ const url = latestClientBundleAnalysisServerUrl;
59
+ latestClientBundleAnalysisServerUrl = undefined;
60
+ return url;
61
+ };
13
62
 
14
63
  export const getClientBundleAnalysisReportPaths = (
15
64
  app: App,
@@ -23,19 +72,25 @@ export const getClientBundleAnalysisReportPaths = (
23
72
  export const createClientBundleAnalysisPlugins = (app: App, outputTarget: 'dev' | 'bin'): RspackPluginInstance[] => {
24
73
  if (!isBundleAnalysisEnabled()) return [];
25
74
 
75
+ latestClientBundleAnalysisServerUrl = undefined;
76
+
26
77
  const { reportPath, statsPath } = getClientBundleAnalysisReportPaths(app, outputTarget);
78
+ const analyzerMode = getBundleAnalysisMode();
27
79
 
28
80
  fs.ensureDirSync(path.dirname(reportPath));
29
81
 
30
82
  return [
31
83
  new BundleAnalyzerPlugin({
32
- analyzerMode: 'static',
84
+ analyzerMode,
85
+ analyzerHost: getBundleAnalysisServerHost(),
86
+ analyzerPort: getBundleAnalysisServerPort(),
33
87
  openAnalyzer: false,
34
88
  defaultSizes: 'parsed',
35
89
  reportFilename: reportPath,
36
90
  generateStatsFile: true,
37
91
  statsFilename: statsPath,
38
92
  logLevel: 'info',
93
+ analyzerUrl: createBundleAnalysisServerUrl,
39
94
  }),
40
95
  ];
41
96
  };
@@ -45,6 +45,11 @@ export default function createCommonConfig(
45
45
  ): Configuration {
46
46
  const dev = mode === 'dev';
47
47
  const enableFilesystemCache = dev ? cli.args.cache !== false : cli.args.cache === true;
48
+ const loaderModuleRoots = [
49
+ cli.paths.framework.appNodeModulesRoot,
50
+ cli.paths.framework.frameworkNodeModulesRoot,
51
+ path.join(cli.paths.core.cli, 'node_modules'),
52
+ ].filter((moduleRoot, index, list) => list.indexOf(moduleRoot) === index);
48
53
  const config: Configuration = {
49
54
  // Project root
50
55
  context: app.paths.root,
@@ -55,11 +60,7 @@ export default function createCommonConfig(
55
60
  // Support both install modes:
56
61
  // - npm i: loaders are often hoisted in app/node_modules
57
62
  // - npm link: loaders often live in framework/node_modules
58
- modules: [
59
- app.paths.root + '/node_modules',
60
- cli.paths.core.root + '/node_modules',
61
- cli.paths.core.cli + '/node_modules',
62
- ],
63
+ modules: loaderModuleRoots,
63
64
  mainFields: ['loader', 'main'],
64
65
  },
65
66
 
@@ -9,6 +9,7 @@ import { rspack, type Compiler as RspackCompiler } from '@rspack/core';
9
9
 
10
10
  // Core
11
11
  import app from '../app';
12
+ import cli from '..';
12
13
  import createServerConfig from './server';
13
14
  import createClientConfig from './client';
14
15
  import { TCompileMode, TCompileOutputTarget } from './common';
@@ -62,15 +63,21 @@ export default class Compiler {
62
63
  - Including React, so VSCode shows that JSX is missing
63
64
  */
64
65
  public fixNpmLinkIssues() {
65
- const corePath = path.join(app.paths.root, '/node_modules/proteum');
66
- if (!fs.lstatSync(corePath).isSymbolicLink())
67
- return logVerbose("Not fixing npm issue because proteum wasn't installed with npm link.");
66
+ const installedFrameworkRoot = cli.paths.framework.installedRoot;
67
+
68
+ if (!installedFrameworkRoot || !fs.existsSync(installedFrameworkRoot)) {
69
+ return logVerbose("Not fixing npm link issues because the app can't see an installed Proteum package.");
70
+ }
71
+
72
+ if (!fs.lstatSync(installedFrameworkRoot).isSymbolicLink()) {
73
+ return logVerbose("Not fixing npm link issues because Proteum wasn't installed with npm link.");
74
+ }
68
75
 
69
76
  this.debug && logVerbose(`Fix NPM link issues ...`);
70
77
  const outputPath = app.outputPath(this.outputTarget);
71
78
 
72
- const appModules = path.join(app.paths.root, 'node_modules');
73
- const coreModules = path.join(corePath, 'node_modules');
79
+ const appModules = cli.paths.framework.appNodeModulesRoot;
80
+ const coreModules = cli.paths.framework.frameworkNodeModulesRoot;
74
81
 
75
82
  // When the 5htp package is installed from npm link,
76
83
  // Modules are installed locally and not glbally as with with the 5htp package from NPM.
@@ -51,10 +51,12 @@ const getDevGeneratedRuntimeEntries = (app: App) => ({
51
51
  });
52
52
  const normalizeModulePath = (value?: string) => (value || '').replace(/\\/g, '/');
53
53
  const rewriteFrameworkAliasTargets = (app: App, aliases: Record<string, string | string[]>) => {
54
- const installedCoreRoot = normalizeModulePath(app.paths.root + '/node_modules/proteum');
55
- const activeCoreRoot = normalizeModulePath(cli.paths.core.root);
54
+ const installedCoreRoot = cli.paths.framework.installedRoot
55
+ ? normalizeModulePath(cli.paths.framework.installedRoot)
56
+ : undefined;
57
+ const activeCoreRoot = normalizeModulePath(cli.paths.framework.activeRoot);
56
58
 
57
- if (installedCoreRoot === activeCoreRoot) return aliases;
59
+ if (!installedCoreRoot || installedCoreRoot === activeCoreRoot) return aliases;
58
60
 
59
61
  const rewriteCandidate = (candidate: string) =>
60
62
  normalizeModulePath(candidate).startsWith(installedCoreRoot + '/')
@@ -80,12 +82,11 @@ export default function createCompiler(
80
82
  debug && console.info(`Creating compiler for server (${mode}).`);
81
83
  const dev = mode === 'dev';
82
84
  const outputPath = app.outputPath(outputTarget);
83
- const installedCoreRoot = app.paths.root + '/node_modules/proteum';
84
- const frameworkRoots = [cli.paths.core.root, installedCoreRoot];
85
+ const frameworkRoots = cli.paths.getFrameworkRoots();
85
86
  const transpileModuleDirectories = app.transpileModuleDirectories;
86
87
 
87
88
  const commonConfig = createCommonConfig(app, 'server', mode, outputTarget);
88
- const { aliases } = app.aliases.server.forWebpack({ modulesPath: app.paths.root + '/node_modules' });
89
+ const { aliases } = app.aliases.server.forWebpack({ modulesPath: cli.paths.framework.appNodeModulesRoot });
89
90
  const resolvedAliases = rewriteFrameworkAliasTargets(app, aliases);
90
91
 
91
92
  // We're not supposed in any case to import client services from server
package/cli/index.ts CHANGED
@@ -3,21 +3,62 @@ process.traceDeprecation = true;
3
3
  import { Cli } from 'clipanion';
4
4
 
5
5
  import cli from './context';
6
+ import { resolveFrameworkInstallInfo } from './paths';
6
7
  import { proteumCommandNames } from './presentation/commands';
7
8
  import { renderCliOverview, renderCommandHelp, resolveCustomHelpRequest } from './presentation/help';
9
+ import { renderCliWelcomeBanner } from './presentation/welcome';
8
10
  import { normalizeHelpArgv, normalizeLegacyArgv } from './runtime/argv';
9
11
  import { createCli, registeredCommands } from './runtime/commands';
10
12
 
13
+ const formatInvocation = (argv: string[]) => ['proteum', ...argv].join(' ').trim();
14
+
15
+ const shouldRenderSharedWelcomeBanner = ({
16
+ argv,
17
+ helpRequestKind,
18
+ }: {
19
+ argv: string[];
20
+ helpRequestKind: 'none' | 'overview' | 'command';
21
+ }) => {
22
+ if (helpRequestKind !== 'none') return true;
23
+ if (argv[0] !== 'dev') return true;
24
+
25
+ if (argv.length === 1) return false;
26
+
27
+ const action = argv[1];
28
+ if (action.startsWith('-')) return false;
29
+
30
+ return action === 'list' || action === 'stop';
31
+ };
32
+
11
33
  export const runCli = async (argv: string[] = process.argv.slice(2)) => {
12
34
  const normalizedArgv = normalizeHelpArgv(normalizeLegacyArgv(argv), proteumCommandNames);
13
- const clipanion = createCli(String(cli.packageJson.version || ''));
35
+ const version = String(cli.packageJson.version || '');
36
+ const proteumInstall = resolveFrameworkInstallInfo({
37
+ appRoot: cli.paths.appRoot,
38
+ framework: cli.paths.framework,
39
+ });
40
+ const clipanion = createCli(version);
14
41
  const initAvailable = true;
15
42
  const helpRequest = resolveCustomHelpRequest(normalizedArgv);
43
+ const shouldRenderWelcomeBanner = shouldRenderSharedWelcomeBanner({
44
+ argv: normalizedArgv,
45
+ helpRequestKind: helpRequest.kind,
46
+ });
47
+
48
+ if (shouldRenderWelcomeBanner) {
49
+ process.stderr.write(
50
+ `${await renderCliWelcomeBanner({
51
+ command: formatInvocation(normalizedArgv),
52
+ installSummary: proteumInstall.summary,
53
+ version,
54
+ })}\n\n`,
55
+ );
56
+ }
16
57
 
17
58
  if (helpRequest.kind === 'overview') {
18
59
  process.stdout.write(
19
60
  await renderCliOverview({
20
- version: String(cli.packageJson.version || ''),
61
+ version,
21
62
  workdir: process.cwd(),
22
63
  initAvailable,
23
64
  }),