@mainset/cli 0.4.4 → 0.5.0-rc.1
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.
- package/dist/esm/commands/index.mjs +1 -0
- package/dist/esm/commands/init/index.mjs +1 -0
- package/dist/esm/commands/init/init.mjs +113 -0
- package/dist/esm/mainset-cli.mjs +2 -1
- package/dist/types/commands/index.d.mts +1 -0
- package/dist/types/commands/init/index.d.mts +1 -0
- package/dist/types/commands/init/init.d.mts +3 -0
- package/package.json +3 -2
- package/src/commands/init/templates/application/.env.example +1 -0
- package/src/commands/init/templates/application/config/proxy.config.d.mts +5 -0
- package/src/commands/init/templates/application/config/proxy.config.mjs +10 -0
- package/src/commands/init/templates/application/config/serve-static.config.mjs +7 -0
- package/src/commands/init/templates/application/config/ssr-server.config.mts +57 -0
- package/src/commands/init/templates/application/package.json +24 -0
- package/src/commands/init/templates/application/src/@types/bundler-webpack.d.ts +1 -0
- package/src/commands/init/templates/application/src/app.browser.tsx +7 -0
- package/src/commands/init/templates/application/src/app.server.tsx +15 -0
- package/src/commands/init/templates/application/src/index.csr.ts +9 -0
- package/src/commands/init/templates/application/src/index.ssr.ts +9 -0
- package/src/commands/init/templates/application/src/index.template.html +20 -0
- package/src/commands/init/templates/application/src/pages/Home/HomePage.tsx +26 -0
- package/src/commands/init/templates/application/src/pages/Home/home-page.module.scss +9 -0
- package/src/commands/init/templates/application/src/pages/Home/index.ts +1 -0
- package/src/commands/init/templates/application/src/pages/index.ts +1 -0
- package/src/commands/init/templates/application/src/styles/global.scss +1 -0
- package/src/commands/init/templates/application/src/styles/normalize.scss +8 -0
- package/src/commands/init/templates/application/tsconfig.json +9 -0
- package/src/commands/init/templates/configuration/.dockerignore +34 -0
- package/src/commands/init/templates/configuration/.husky/commit-msg +1 -0
- package/src/commands/init/templates/configuration/.husky/pre-commit +1 -0
- package/src/commands/init/templates/configuration/.prettierignore +25 -0
- package/src/commands/init/templates/configuration/Dockerfile +93 -0
- package/src/commands/init/templates/configuration/commitlint.config.mjs +3 -0
- package/src/commands/init/templates/configuration/compose.yaml +59 -0
- package/src/commands/init/templates/configuration/eslint.config.mjs +6 -0
- package/src/commands/init/templates/configuration/lint-staged.config.mjs +3 -0
- package/src/commands/init/templates/configuration/prettier.config.mjs +5 -0
- package/src/commands/init/templates/configuration/stylelint.config.mjs +6 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './init.mjs';
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { stdin as input, stdout as output } from 'node:process';
|
|
5
|
+
import * as readline from 'node:readline/promises';
|
|
6
|
+
import { runtimePathById } from '../../runtime/index.mjs';
|
|
7
|
+
import { consoleColorize, initProcessCatchErrorLogger, } from '../../utils/index.mjs';
|
|
8
|
+
async function handleConfigurationDependencies(targetDir) {
|
|
9
|
+
const rl = readline.createInterface({ input, output });
|
|
10
|
+
let shouldInstall = true;
|
|
11
|
+
try {
|
|
12
|
+
const answer = await rl.question('Do you want to install development dependencies (@mainset/dev-stack-fe etc.)? (Y/n) ');
|
|
13
|
+
shouldInstall = answer.trim().toLowerCase() !== 'n';
|
|
14
|
+
}
|
|
15
|
+
catch (_a) {
|
|
16
|
+
// default true
|
|
17
|
+
}
|
|
18
|
+
if (shouldInstall) {
|
|
19
|
+
let packageManager = 'npm';
|
|
20
|
+
try {
|
|
21
|
+
const answer = await rl.question('Which package manager do you use? (npm/pnpm/yarn/bun/other) [npm]: ');
|
|
22
|
+
packageManager = answer.trim().toLowerCase() || 'npm';
|
|
23
|
+
}
|
|
24
|
+
catch (_b) {
|
|
25
|
+
// default npm
|
|
26
|
+
}
|
|
27
|
+
rl.close();
|
|
28
|
+
let installCommand = '';
|
|
29
|
+
const packages = [
|
|
30
|
+
'@mainset/dev-stack-fe',
|
|
31
|
+
'@mainset/cli',
|
|
32
|
+
'cross-env',
|
|
33
|
+
'eslint',
|
|
34
|
+
'husky',
|
|
35
|
+
'prettier',
|
|
36
|
+
'stylelint',
|
|
37
|
+
].join(' ');
|
|
38
|
+
if (['npm', 'pnpm', 'yarn', 'bun'].includes(packageManager)) {
|
|
39
|
+
const cmd = packageManager === 'npm' ? 'install' : 'add';
|
|
40
|
+
installCommand = `${packageManager} ${cmd} -D ${packages}`;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
// assume 'other' is the binary name user typed if not in basic list
|
|
44
|
+
installCommand = `${packageManager} i -D ${packages}`;
|
|
45
|
+
}
|
|
46
|
+
console.log(consoleColorize('BLUE', `\nInstalling dependencies with: ${installCommand}\n`));
|
|
47
|
+
try {
|
|
48
|
+
execSync(installCommand, { cwd: targetDir, stdio: 'inherit' });
|
|
49
|
+
console.log(consoleColorize('BRIGHT_GREEN', '\n✅ Dependencies installed successfully.\n'));
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
console.error(consoleColorize('BRIGHT_RED', `\n❌ Failed to install dependencies: ${error.message}\n`));
|
|
53
|
+
}
|
|
54
|
+
// Close readline if not installing
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
rl.close();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function registerInitCommand(program) {
|
|
61
|
+
program
|
|
62
|
+
.command('init [appName]')
|
|
63
|
+
.description('Run init project or configuration process')
|
|
64
|
+
.option('-m, --mode <type>', 'Initialization mode: app, package or config', 'application')
|
|
65
|
+
.option('-p, --path <relativePath>', 'Initialization path', './')
|
|
66
|
+
.action(async (appName, options) => {
|
|
67
|
+
// Use runtimePathById.root to resolve the target directory relative to the project root
|
|
68
|
+
const targetDir = path.resolve(runtimePathById.root, options.path, appName || '');
|
|
69
|
+
if (options.mode === 'application') {
|
|
70
|
+
if (!appName) {
|
|
71
|
+
console.error(consoleColorize('BRIGHT_RED', `\n[mainset cli][init] App name is required for application initialization.`));
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
// ========== Application mode ==========
|
|
75
|
+
console.log('\n🏗️ [mainset cli] init: application');
|
|
76
|
+
try {
|
|
77
|
+
// Use runtimePathById.msCLISrc to resolve the template directory relative to the CLI runtime
|
|
78
|
+
const templateDir = path.resolve(runtimePathById.msCLISrc, '../../../src/commands/init/templates/application');
|
|
79
|
+
if (fs.existsSync(targetDir)) {
|
|
80
|
+
console.error(consoleColorize('BRIGHT_YELLOW', `\n[mainset cli][init] Directory "${appName}" already exists at "${targetDir}". Aborting.`));
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
fs.cpSync(templateDir, targetDir, { recursive: true });
|
|
84
|
+
console.log(`\n✅ Application "${appName}" initialized successfully.\n`);
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
initProcessCatchErrorLogger('init', error, 'application');
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else if (options.mode === 'config') {
|
|
91
|
+
// ========== Configuration mode ==========
|
|
92
|
+
console.log('\n🏗️ [mainset cli] init: configuration');
|
|
93
|
+
try {
|
|
94
|
+
const templateDir = path.resolve(runtimePathById.msCLISrc, '../../../src/commands/init/templates/configuration');
|
|
95
|
+
if (!fs.existsSync(templateDir)) {
|
|
96
|
+
console.log(consoleColorize('BRIGHT_YELLOW', `\n[mainset cli][init] Configuration template not found at ${templateDir}`));
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
fs.cpSync(templateDir, targetDir, { recursive: true });
|
|
100
|
+
console.log('\n✅ Configuration initialized successfully.\n');
|
|
101
|
+
await handleConfigurationDependencies(targetDir);
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
initProcessCatchErrorLogger('init', error, 'configuration');
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
console.error(consoleColorize('BRIGHT_YELLOW', `[mainset cli][init] Unknown init mode: "${options.mode}"`));
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
export { registerInitCommand };
|
package/dist/esm/mainset-cli.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from 'commander';
|
|
3
|
-
import { registerNodeSourcerCommand, registerSourceCodeCommand, registerWebAppCommand, } from './commands/index.mjs';
|
|
3
|
+
import { registerInitCommand, registerNodeSourcerCommand, registerSourceCodeCommand, registerWebAppCommand, } from './commands/index.mjs';
|
|
4
4
|
import { verifyOrSetNodeEnv } from './utils/index.mjs';
|
|
5
5
|
// !IMPORTANT: Set NODE_ENV is case it is not passed
|
|
6
6
|
verifyOrSetNodeEnv();
|
|
@@ -9,6 +9,7 @@ program
|
|
|
9
9
|
.name('ms-cli')
|
|
10
10
|
.description('CLI to manage frontend tooling and infrastructure')
|
|
11
11
|
.version('0.1.0');
|
|
12
|
+
registerInitCommand(program);
|
|
12
13
|
registerSourceCodeCommand(program);
|
|
13
14
|
registerNodeSourcerCommand(program);
|
|
14
15
|
registerWebAppCommand(program);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './init.mjs';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mainset/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0-rc.1",
|
|
4
4
|
"description": "A unified CLI tool for accelerating development, based on mainset vision of front-end infrastructure",
|
|
5
5
|
"homepage": "https://github.com/mainset/dev-stack-fe/tree/main/packages/cli",
|
|
6
6
|
"bugs": {
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"type": "module",
|
|
16
16
|
"sideEffects": false,
|
|
17
17
|
"files": [
|
|
18
|
-
"dist"
|
|
18
|
+
"dist",
|
|
19
|
+
"src/commands/init/templates"
|
|
19
20
|
],
|
|
20
21
|
"exports": {
|
|
21
22
|
"./runtime": {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
API_REMOTE_URL=https://httpbin.org
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { runtimePathById } from '@mainset/cli/runtime';
|
|
2
|
+
import type { SSRConfigParams } from '@mainset/cli/ssr-server';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import ReactDOMServer from 'react-dom/server';
|
|
6
|
+
|
|
7
|
+
import provideServerReactApp from '../src/app.server';
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
9
|
+
// @ts-ignore
|
|
10
|
+
import proxyConfigByPath from './proxy.config.mjs';
|
|
11
|
+
|
|
12
|
+
const ssrServerConfig: SSRConfigParams = {
|
|
13
|
+
serveStatics: [
|
|
14
|
+
{
|
|
15
|
+
rootPath: path.join(runtimePathById.dist, 'public'),
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
proxyConfigByPath,
|
|
19
|
+
renderSSRContentByPath: {
|
|
20
|
+
'/{*any}': ({ reqUrl, fullUrl }) =>
|
|
21
|
+
new Promise((resolve, reject) => {
|
|
22
|
+
fs.readFile(
|
|
23
|
+
path.join(runtimePathById.dist, 'public', 'server.html'),
|
|
24
|
+
'utf8',
|
|
25
|
+
async (err, htmlData) => {
|
|
26
|
+
if (err) {
|
|
27
|
+
return reject(err);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Const context = {}; // Context to track SSR rendering information
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
// Pass both requestUrl (relative) and fullUrl to provider of React App for server-side rendering
|
|
34
|
+
const appHtml = await provideServerReactApp({
|
|
35
|
+
reqUrl,
|
|
36
|
+
fullUrl,
|
|
37
|
+
// Context
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const content = htmlData.replace(
|
|
41
|
+
'<div id="example-react"></div>',
|
|
42
|
+
`<div id="example-react">${ReactDOMServer.renderToString(
|
|
43
|
+
appHtml,
|
|
44
|
+
)}</div>`,
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
return resolve(content);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
return reject(error);
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
);
|
|
53
|
+
}),
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export default ssrServerConfig;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"engines": {
|
|
3
|
+
"node": ">=24.14.0"
|
|
4
|
+
},
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "cross-env NODE_ENV=development ms-cli web-app --exec serve",
|
|
7
|
+
"dev:csr": "cross-env NODE_ENV=development ms-cli web-app --exec serve --serveMode csr",
|
|
8
|
+
"build": "ms-cli web-app --exec build",
|
|
9
|
+
"build:csr": "ms-cli web-app --exec build --serveMode csr",
|
|
10
|
+
"serve": "ms-cli web-app --exec serve-static",
|
|
11
|
+
"serve:csr": "ms-cli web-app --exec serve-static --serveMode csr"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"react": "^19.2.0",
|
|
15
|
+
"react-dom": "^19.2.0"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@mainset/bundler-webpack": "^0.2.0",
|
|
19
|
+
"@mainset/cli": "^0.4.4",
|
|
20
|
+
"@mainset/dev-stack-fe": "^0.3.1",
|
|
21
|
+
"@types/react": "^19.2.2",
|
|
22
|
+
"@types/react-dom": "^19.2.1"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '@mainset/bundler-webpack/global-types';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import BrowserReactApp from './app.browser';
|
|
2
|
+
|
|
3
|
+
type ProvideServerReactAppParams = {
|
|
4
|
+
reqUrl: string;
|
|
5
|
+
fullUrl: string;
|
|
6
|
+
// Context?: Record<string, any>;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const provideServerReactApp = (params?: ProvideServerReactAppParams) => {
|
|
10
|
+
console.log({ params });
|
|
11
|
+
|
|
12
|
+
return BrowserReactApp;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export default provideServerReactApp;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import ReactDOMClient from 'react-dom/client';
|
|
2
|
+
|
|
3
|
+
import BrowserReactApp from './app.browser';
|
|
4
|
+
|
|
5
|
+
const targetContainer = document.getElementById('example-react');
|
|
6
|
+
|
|
7
|
+
if (targetContainer) {
|
|
8
|
+
ReactDOMClient.createRoot(targetContainer).render(BrowserReactApp);
|
|
9
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<!-- Required meta tags -->
|
|
5
|
+
<meta charset="utf-8" />
|
|
6
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
7
|
+
<meta
|
|
8
|
+
name="viewport"
|
|
9
|
+
content="width=device-width, initial-scale=1, shrink-to-fit=no"
|
|
10
|
+
/>
|
|
11
|
+
|
|
12
|
+
<!-- styles -->
|
|
13
|
+
</head>
|
|
14
|
+
|
|
15
|
+
<body>
|
|
16
|
+
<div id="example-react"></div>
|
|
17
|
+
|
|
18
|
+
<!-- SPA -->
|
|
19
|
+
</body>
|
|
20
|
+
</html>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
import * as styles from './home-page.module.scss';
|
|
4
|
+
|
|
5
|
+
const HomePage = () => {
|
|
6
|
+
const [apiResponse, setApiResponse] = React.useState<{
|
|
7
|
+
origin: string;
|
|
8
|
+
} | null>(null);
|
|
9
|
+
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
fetch('/api-example/ip')
|
|
12
|
+
.then((response) => response.json())
|
|
13
|
+
.then((responseAsJson) => {
|
|
14
|
+
setApiResponse(responseAsJson);
|
|
15
|
+
});
|
|
16
|
+
}, []);
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<div className={styles['home-page__welcome-section']}>
|
|
20
|
+
<h1>Hello world</h1>
|
|
21
|
+
<p>Your IP is: {apiResponse?.origin || '...'}</p>
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export { HomePage };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './HomePage';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { HomePage } from './Home';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@use './normalize.scss';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Include any files or directories that you don't want to be copied to your
|
|
2
|
+
# container here (e.g., local build artifacts, temporary files, etc.).
|
|
3
|
+
#
|
|
4
|
+
# For more help, visit the .dockerignore file reference guide at
|
|
5
|
+
# https://docs.docker.com/go/build-context-dockerignore/
|
|
6
|
+
|
|
7
|
+
**/.classpath
|
|
8
|
+
**/.dockerignore
|
|
9
|
+
**/.env
|
|
10
|
+
**/.git
|
|
11
|
+
**/.gitignore
|
|
12
|
+
**/.project
|
|
13
|
+
**/.settings
|
|
14
|
+
**/.toolstarget
|
|
15
|
+
**/.vs
|
|
16
|
+
**/.vscode
|
|
17
|
+
**/.next
|
|
18
|
+
**/.cache
|
|
19
|
+
**/*.*proj.user
|
|
20
|
+
**/*.dbmdl
|
|
21
|
+
**/*.jfm
|
|
22
|
+
**/charts
|
|
23
|
+
**/docker-compose*
|
|
24
|
+
**/compose.y*ml
|
|
25
|
+
**/Dockerfile*
|
|
26
|
+
**/node_modules
|
|
27
|
+
**/npm-debug.log
|
|
28
|
+
**/obj
|
|
29
|
+
**/secrets.dev.yaml
|
|
30
|
+
**/values.dev.yaml
|
|
31
|
+
**/build
|
|
32
|
+
**/dist
|
|
33
|
+
LICENSE
|
|
34
|
+
README.md
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
npx commitlint@^19 --edit
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
npx lint-staged@^16
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# general
|
|
2
|
+
LICENSE
|
|
3
|
+
.gitignore
|
|
4
|
+
.keep
|
|
5
|
+
|
|
6
|
+
# configuration files
|
|
7
|
+
.dockerignore
|
|
8
|
+
Dockerfile
|
|
9
|
+
|
|
10
|
+
# package manager
|
|
11
|
+
pnpm-lock.yaml
|
|
12
|
+
.npmrc.example
|
|
13
|
+
|
|
14
|
+
# packages
|
|
15
|
+
.prettierignore
|
|
16
|
+
.husky
|
|
17
|
+
|
|
18
|
+
# files
|
|
19
|
+
*.sh
|
|
20
|
+
*.toml
|
|
21
|
+
*.env*
|
|
22
|
+
|
|
23
|
+
# images
|
|
24
|
+
*.ico
|
|
25
|
+
*.svg
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# syntax=docker/dockerfile:1
|
|
2
|
+
|
|
3
|
+
# Comments are provided throughout this file to help you get started.
|
|
4
|
+
# If you need more help, visit the Dockerfile reference guide at
|
|
5
|
+
# https://docs.docker.com/go/dockerfile-reference/
|
|
6
|
+
|
|
7
|
+
# Want to help us make this template better? Share your feedback here: https://forms.gle/ybq9Krt8jtBL3iCk7
|
|
8
|
+
|
|
9
|
+
ARG NODE_VERSION=24.14.0
|
|
10
|
+
ARG PNPM_VERSION=10.30.3
|
|
11
|
+
|
|
12
|
+
FROM node:${NODE_VERSION}-alpine as base
|
|
13
|
+
|
|
14
|
+
# Install pnpm.
|
|
15
|
+
RUN --mount=type=cache,target=/root/.npm \
|
|
16
|
+
npm install -g pnpm@${PNPM_VERSION}
|
|
17
|
+
|
|
18
|
+
WORKDIR /usr/src/web-app--react
|
|
19
|
+
|
|
20
|
+
################################################################################
|
|
21
|
+
# Create a stage for installing production dependencies.
|
|
22
|
+
FROM base AS install-dependencies
|
|
23
|
+
|
|
24
|
+
# Download dependencies as a separate step to take advantage of Docker's caching.
|
|
25
|
+
# Leverage a cache mount to /root/.local/share/pnpm/store to speed up subsequent builds.
|
|
26
|
+
# Leverage a bind mounts to package.json and pnpm-lock.yaml to avoid having to copy them into
|
|
27
|
+
# into this layer.
|
|
28
|
+
RUN \
|
|
29
|
+
# mount root files
|
|
30
|
+
--mount=type=bind,source=package.json,target=package.json \
|
|
31
|
+
--mount=type=bind,source=pnpm-lock.yaml,target=pnpm-lock.yaml \
|
|
32
|
+
# mount cache and install dependencies
|
|
33
|
+
--mount=type=cache,target=/root/.local/share/pnpm/store \
|
|
34
|
+
(\
|
|
35
|
+
# Install dependencies with pnpm
|
|
36
|
+
# pnpm install --prod --frozen-lockfile
|
|
37
|
+
pnpm install --frozen-lockfile \
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
################################################################################
|
|
41
|
+
# Create a stage for building the packages source code for Server Side Rendering
|
|
42
|
+
FROM install-dependencies as build-ssr
|
|
43
|
+
|
|
44
|
+
# Copy the rest of the source files into the image.
|
|
45
|
+
COPY . .
|
|
46
|
+
|
|
47
|
+
# Run the build script.
|
|
48
|
+
RUN pnpm run build
|
|
49
|
+
|
|
50
|
+
################################################################################
|
|
51
|
+
# Create a new {production} stage that will be running the application
|
|
52
|
+
FROM install-dependencies AS production
|
|
53
|
+
|
|
54
|
+
# Use production node environment
|
|
55
|
+
ENV NODE_ENV production
|
|
56
|
+
|
|
57
|
+
# Run the application as a non-root user.
|
|
58
|
+
USER node
|
|
59
|
+
|
|
60
|
+
# Copy compiled {/dist} code from the {build} Docker stage into the image.
|
|
61
|
+
COPY --chown=node:node --from=build-ssr /usr/src/web-app--react/dist ./dist
|
|
62
|
+
# Copy other required files and folders to launch the application
|
|
63
|
+
COPY ./package.json ./
|
|
64
|
+
COPY ./config ./config
|
|
65
|
+
|
|
66
|
+
# Expose the port that the application listens on.
|
|
67
|
+
EXPOSE 8080
|
|
68
|
+
|
|
69
|
+
# Run the application.
|
|
70
|
+
CMD ["pnpm", "run", "serve"]
|
|
71
|
+
|
|
72
|
+
################################################################################
|
|
73
|
+
# Create a new {development} stage that will be running the application
|
|
74
|
+
FROM install-dependencies AS development
|
|
75
|
+
|
|
76
|
+
# Use production node environment
|
|
77
|
+
ENV NODE_ENV development
|
|
78
|
+
|
|
79
|
+
# Run the application as a non-root user.
|
|
80
|
+
USER node
|
|
81
|
+
|
|
82
|
+
# Copy compiled {/dist} code from the {build} Docker stage into the image.
|
|
83
|
+
COPY --chown=node:node --from=build-ssr /usr/src/web-app--react/dist ./dist
|
|
84
|
+
|
|
85
|
+
# Copy the rest of the source files into the image.
|
|
86
|
+
# !IMPORTANT: the {--chown=node:node} required, server crashes on file change, files re-generated with root permissions
|
|
87
|
+
COPY --chown=node:node . .
|
|
88
|
+
|
|
89
|
+
# Expose the port that the application listens on.
|
|
90
|
+
EXPOSE 8080
|
|
91
|
+
|
|
92
|
+
# Run the application.
|
|
93
|
+
CMD ["pnpm", "run", "dev"]
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Comments are provided throughout this file to help you get started.
|
|
2
|
+
# If you need more help, visit the Docker Compose reference guide at
|
|
3
|
+
# https://docs.docker.com/go/compose-spec-reference/
|
|
4
|
+
|
|
5
|
+
# Here the instructions define your application as a service called "server".
|
|
6
|
+
# This service is built from the Dockerfile in the current directory.
|
|
7
|
+
# You can add other services your application may depend on here, such as a
|
|
8
|
+
# database or a cache. For examples, see the Awesome Compose repository:
|
|
9
|
+
# https://github.com/docker/awesome-compose
|
|
10
|
+
services:
|
|
11
|
+
web-app--react:
|
|
12
|
+
build:
|
|
13
|
+
context: .
|
|
14
|
+
target: production
|
|
15
|
+
# NOTE: start production service by default without {--profile} flag
|
|
16
|
+
profiles: [prod]
|
|
17
|
+
env_file:
|
|
18
|
+
- ./.env
|
|
19
|
+
ports:
|
|
20
|
+
- 8080:8080
|
|
21
|
+
labels:
|
|
22
|
+
- 'traefik.enable=true'
|
|
23
|
+
- 'traefik.http.routers.web-app--react.rule=Host(`react-boilerplate.mainset.${DEV__DOMAIN_LOCALHOST:-localhost}`)'
|
|
24
|
+
- 'traefik.http.services.web-app--react.loadbalancer.server.port=8080'
|
|
25
|
+
networks:
|
|
26
|
+
- network--dev
|
|
27
|
+
web-app--react-dev:
|
|
28
|
+
extends: web-app--react
|
|
29
|
+
build:
|
|
30
|
+
target: development
|
|
31
|
+
env_file:
|
|
32
|
+
- ./.env.development
|
|
33
|
+
volumes:
|
|
34
|
+
# Bind Mount code for Live Update
|
|
35
|
+
- ./src:/usr/src/web-app--react/src
|
|
36
|
+
# !IMPORTANT: ensure that the volume mounting does not overwrite any of the {node_modules} directory
|
|
37
|
+
- /usr/src/web-app--react/node_modules
|
|
38
|
+
# !IMPORTANT: ensure that the volume mounting does not overwrite any of the {dist} static directory
|
|
39
|
+
- /usr/src/web-app--react/dist
|
|
40
|
+
profiles: [dev]
|
|
41
|
+
|
|
42
|
+
# development service
|
|
43
|
+
# make the service available under custom domain on local computer
|
|
44
|
+
traefik:
|
|
45
|
+
image: traefik:v2.5
|
|
46
|
+
command:
|
|
47
|
+
- '--api.insecure=true'
|
|
48
|
+
- '--providers.docker=true'
|
|
49
|
+
- '--entrypoints.web.address=:80'
|
|
50
|
+
ports:
|
|
51
|
+
- '80:80'
|
|
52
|
+
volumes:
|
|
53
|
+
- '/var/run/docker.sock:/var/run/docker.sock:ro'
|
|
54
|
+
networks:
|
|
55
|
+
- network--dev
|
|
56
|
+
|
|
57
|
+
# development
|
|
58
|
+
networks:
|
|
59
|
+
network--dev:
|