create-lupine 1.0.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 (62) hide show
  1. package/index.js +277 -0
  2. package/package.json +18 -0
  3. package/templates/common/.env +57 -0
  4. package/templates/common/.env.development +7 -0
  5. package/templates/common/.env.mobile +10 -0
  6. package/templates/common/.env.production +7 -0
  7. package/templates/common/apps/server/src/app-loader.ts +41 -0
  8. package/templates/common/apps/server/src/fetch-data.ts +20 -0
  9. package/templates/common/apps/server/src/index.ts +66 -0
  10. package/templates/common/apps/server/src/server-env-keys.ts +22 -0
  11. package/templates/common/dev/dev-watch.js +422 -0
  12. package/templates/doc-starter/api/resources/config_default.json +6 -0
  13. package/templates/doc-starter/api/resources/install.sqlite.sql +4 -0
  14. package/templates/doc-starter/api/src/index.ts +15 -0
  15. package/templates/doc-starter/api/src/resources/config_default.json +6 -0
  16. package/templates/doc-starter/api/src/resources/install.sqlite.sql +4 -0
  17. package/templates/doc-starter/lupine.json +33 -0
  18. package/templates/doc-starter/package.json +13 -0
  19. package/templates/doc-starter/web/assets/android-chrome-192x192.png +0 -0
  20. package/templates/doc-starter/web/assets/apple-touch-icon.png +0 -0
  21. package/templates/doc-starter/web/assets/favicon-16x16.png +0 -0
  22. package/templates/doc-starter/web/assets/favicon-32x32.png +0 -0
  23. package/templates/doc-starter/web/assets/favicon.ico +0 -0
  24. package/templates/doc-starter/web/assets/site.webmanifest +14 -0
  25. package/templates/doc-starter/web/github-pj-name/index.html +21 -0
  26. package/templates/doc-starter/web/github-pj-name/index.tsx +35 -0
  27. package/templates/doc-starter/web/markdown/en/essentials/index.md +6 -0
  28. package/templates/doc-starter/web/markdown/en/essentials/list.md +18 -0
  29. package/templates/doc-starter/web/markdown/en/guide/install.md +18 -0
  30. package/templates/doc-starter/web/markdown/en/guide/started.md +22 -0
  31. package/templates/doc-starter/web/markdown/en/index.md +42 -0
  32. package/templates/doc-starter/web/markdown/index.md +7 -0
  33. package/templates/doc-starter/web/markdown/zh/essentials/index.md +6 -0
  34. package/templates/doc-starter/web/markdown/zh/essentials/list.md +18 -0
  35. package/templates/doc-starter/web/markdown/zh/guide/install.md +18 -0
  36. package/templates/doc-starter/web/markdown/zh/guide/started.md +22 -0
  37. package/templates/doc-starter/web/markdown/zh/index.md +42 -0
  38. package/templates/doc-starter/web/src/client-env-keys.ts +5 -0
  39. package/templates/doc-starter/web/src/index.html +21 -0
  40. package/templates/doc-starter/web/src/index.tsx +33 -0
  41. package/templates/doc-starter/web/src/markdown-built/en/essentials/index.html +0 -0
  42. package/templates/doc-starter/web/src/markdown-built/en/essentials/list.html +8 -0
  43. package/templates/doc-starter/web/src/markdown-built/en/guide/install.html +8 -0
  44. package/templates/doc-starter/web/src/markdown-built/en/guide/started.html +12 -0
  45. package/templates/doc-starter/web/src/markdown-built/en/index.html +0 -0
  46. package/templates/doc-starter/web/src/markdown-built/index.html +0 -0
  47. package/templates/doc-starter/web/src/markdown-built/markdown-config.ts +25 -0
  48. package/templates/doc-starter/web/src/markdown-built/zh/essentials/index.html +0 -0
  49. package/templates/doc-starter/web/src/markdown-built/zh/essentials/list.html +8 -0
  50. package/templates/doc-starter/web/src/markdown-built/zh/guide/install.html +8 -0
  51. package/templates/doc-starter/web/src/markdown-built/zh/guide/started.html +12 -0
  52. package/templates/doc-starter/web/src/markdown-built/zh/index.html +0 -0
  53. package/templates/doc-starter/web/src/styles/base-css.ts +15 -0
  54. package/templates/hello-world/api/resources/config_default.json +6 -0
  55. package/templates/hello-world/api/resources/install.sqlite.sql +4 -0
  56. package/templates/hello-world/api/src/index.ts +4 -0
  57. package/templates/hello-world/api/src/service/root-api.ts +18 -0
  58. package/templates/hello-world/lupine.json +23 -0
  59. package/templates/hello-world/web/assets/favicon.ico +0 -0
  60. package/templates/hello-world/web/package.json +6 -0
  61. package/templates/hello-world/web/src/index.html +16 -0
  62. package/templates/hello-world/web/src/index.tsx +30 -0
@@ -0,0 +1,422 @@
1
+ // esbuild command tools
2
+
3
+ const esbuild = require('esbuild');
4
+ const path = require('path');
5
+ const fs = require('fs/promises');
6
+ const {
7
+ runCmd,
8
+ copyFolder,
9
+ sendRequest,
10
+ loadEnv,
11
+ readJson,
12
+ pathExists,
13
+ cpIndexHtml,
14
+ pluginIfelse,
15
+ markdownProcessOnEnd,
16
+ } = require('lupine.api/dev');
17
+
18
+ const triggerHandle = {
19
+ restart: null,
20
+ refresh: null,
21
+ };
22
+
23
+ // restart server for server code changes
24
+ const triggerReStartServer = async (isDev, npmCmd, httpPort) => {
25
+ if (triggerHandle.restart) {
26
+ clearTimeout(triggerHandle.restart);
27
+ }
28
+ triggerHandle.restart = setTimeout(async () => {
29
+ const url = `http://127.0.0.1:${httpPort}/debug/shutdown`;
30
+ await sendRequest(url, isDev ? 2 : 0);
31
+ console.log('[dev-server] ReStart Server: ', url);
32
+ isDev && runCmd(npmCmd);
33
+ }, 500);
34
+ };
35
+
36
+ // refresh server for client code changes
37
+ const triggerRefreshServer = async (isDev, httpPort) => {
38
+ if (triggerHandle.refresh) {
39
+ clearTimeout(triggerHandle.refresh);
40
+ }
41
+ triggerHandle.refresh = setTimeout(async () => {
42
+ const url = `http://127.0.0.1:${httpPort}/debug/refresh`;
43
+ await sendRequest(url, isDev ? 2 : 0);
44
+ console.log('[dev-server] Refresh Server: ', url);
45
+ }, 500);
46
+ };
47
+
48
+ // watch server code changes
49
+ const watchServerPlugin = (isDev, npmCmd, httpPort) => {
50
+ return {
51
+ name: 'watchServerPlugin',
52
+ setup(build) {
53
+ build.onEnd(async (res) => {
54
+ // console.log(`Build meta data: `, res);
55
+ console.log(`[dev-server] Build finished`);
56
+ if (isDev) {
57
+ await triggerReStartServer(isDev, npmCmd, httpPort);
58
+ }
59
+ });
60
+ },
61
+ };
62
+ };
63
+
64
+ // javascript-obfuscator is needed
65
+ const obfuscatePlugin = (isObfuscate, entryPoints = [], skipPaths = []) => {
66
+ if (!isObfuscate) return { name: 'obfuscatePlugin', setup() {} };
67
+
68
+ const JavaScriptObfuscator = require('javascript-obfuscator');
69
+ return {
70
+ name: 'obfuscatePlugin',
71
+ setup(build) {
72
+ build.onLoad({ filter: /\.(js|ts|tsx|jsx)$/ }, async (args) => {
73
+ if (args.path.includes('node_modules')) return null;
74
+ if (skipPaths.some((skipPath) => args.path.includes(skipPath))) {
75
+ console.log(`[dev-server] Skip obfuscate: ${args.path}`);
76
+ return null;
77
+ }
78
+
79
+ const ext = path.extname(args.path);
80
+ console.log(`[dev-server] Obfuscate: ${args.path}`);
81
+ let content = await fs.readFile(args.path, 'utf8');
82
+
83
+ // Transpile TS/JSX to JS first because obfuscator works on JS
84
+ if (['.ts', '.tsx', '.jsx'].includes(ext)) {
85
+ const result = await esbuild.transform(content, {
86
+ loader: ext.substring(1),
87
+ sourcefile: args.path,
88
+ jsx: 'automatic',
89
+ jsxImportSource: 'lupine.web',
90
+ });
91
+ content = result.code;
92
+ }
93
+
94
+ const obfuscationResult = JavaScriptObfuscator.obfuscate(content, {
95
+ compact: true,
96
+ controlFlowFlattening: true,
97
+ controlFlowFlatteningThreshold: 0.75,
98
+ deadCodeInjection: false,
99
+ deadCodeInjectionThreshold: 0,
100
+ // debugProtection: isEntryPoint, // this should be done in code: disableDebug('xxx');
101
+ debugProtectionInterval: 2000,
102
+ // disableConsoleOutput: isEntryPoint, // this should be done in code: disableConsole();
103
+ identifierNamesGenerator: 'hexadecimal',
104
+ stringArray: true,
105
+ stringArrayThreshold: 0.75,
106
+ ignoreImports: true,
107
+ });
108
+
109
+ return {
110
+ contents: obfuscationResult.getObfuscatedCode(),
111
+ loader: 'js',
112
+ };
113
+ });
114
+ },
115
+ };
116
+ };
117
+
118
+ // watch server code changes
119
+ const watchAppLoader = async (isDev, npmCmd, httpPort, serverRootPath) => {
120
+ const cmd = isDev ? esbuild.context : esbuild.build;
121
+ const ctx = await cmd({
122
+ entryPoints: ['apps/server/src/app-loader.ts'],
123
+ // outdir: path.join(serverRootPath, 'server'),
124
+ outfile: path.join(serverRootPath, 'server', 'app-loader.js'),
125
+ platform: 'node',
126
+ sourcemap: !!isDev,
127
+ format: 'cjs',
128
+ bundle: true,
129
+ treeShaking: true,
130
+ metafile: true,
131
+ minify: !isDev,
132
+ plugins: [watchServerPlugin(isDev, npmCmd, httpPort)],
133
+ });
134
+
135
+ isDev && (await ctx.watch());
136
+ isDev && console.log('[dev-server] Watching...');
137
+ };
138
+
139
+ // watch server code changes
140
+ const watchServer = async (isDev, npmCmd, httpPort, serverRootPath) => {
141
+ const cmd = isDev ? esbuild.context : esbuild.build;
142
+ const ctx = await cmd({
143
+ entryPoints: ['apps/server/src/index.ts'],
144
+ outdir: path.join(serverRootPath, 'server'),
145
+ platform: 'node',
146
+ sourcemap: !!isDev,
147
+ format: 'cjs',
148
+ bundle: true,
149
+ treeShaking: true,
150
+ metafile: true,
151
+ external: ['better-sqlite3', 'nodemailer', 'pdfkit', 'sharp'],
152
+ minify: !isDev,
153
+ plugins: [watchServerPlugin(isDev, npmCmd, httpPort)],
154
+ });
155
+
156
+ isDev && (await ctx.watch());
157
+ isDev && console.log('[dev-server] Watching...');
158
+ };
159
+
160
+ // copy files to output directory
161
+ const copyCache = new Map();
162
+ const clientProcessOnEnd = async (saved) => {
163
+ saved.indexHtml &&
164
+ cpIndexHtml(
165
+ saved.indexHtml,
166
+ path.join(saved.outdir, 'index.html'),
167
+ saved.appName,
168
+ saved.isMobile,
169
+ saved.defaultThemeName,
170
+ saved.outdirData,
171
+ saved.outdirSub
172
+ );
173
+
174
+ const assets = saved['copyFiles'];
175
+ if (assets) {
176
+ for (oneDir of assets) {
177
+ await copyFolder(copyCache, oneDir.from, oneDir.to, saved.isDev);
178
+ }
179
+ }
180
+
181
+ if (saved.isDev) {
182
+ await triggerRefreshServer(saved.isDev, saved.httpPort);
183
+ }
184
+ };
185
+
186
+ // watch client code changes
187
+ const watchClientPlugin = (saved) => {
188
+ return {
189
+ name: 'watchClientPlugin',
190
+ setup(build) {
191
+ build.onEnd(async (res) => {
192
+ // console.log(`Build meta data: `, res);
193
+ console.log(`[dev-client:${saved.appName}] Build finished`);
194
+ await clientProcessOnEnd(saved);
195
+ });
196
+ },
197
+ };
198
+ };
199
+
200
+ // plugin for conditional code logic
201
+ /*
202
+ #if MOBILE && DEV
203
+ console.log('this is mobile and dev');
204
+ #endif
205
+ */
206
+ const ifPluginVars = {
207
+ MOBILE: '',
208
+ DEV: '',
209
+ };
210
+ const watchClient = async (saved, isDev, entryPoints, outbase) => {
211
+ const cmd = isDev ? esbuild.context : esbuild.build;
212
+ const ctx = await cmd({
213
+ entryPoints,
214
+ outdir: saved.outdir,
215
+ outbase,
216
+ // entryNames: '[name]-[hash]',
217
+ platform: 'browser',
218
+ sourcemap: !!isDev, // inline
219
+ format: 'iife',
220
+ bundle: true,
221
+ treeShaking: true,
222
+ external: [],
223
+ metafile: true,
224
+ minify: !isDev,
225
+ loader: { '.svg': 'text', '.glsl': 'text', '.png': 'file', '.gif': 'file', '.html': 'text' },
226
+ assetNames: '/pub_assets/[name]-[hash]',
227
+ publicPath: saved.outdirSub,
228
+ jsxImportSource: 'lupine.web',
229
+ jsx: 'automatic',
230
+ target: ['chrome87'],
231
+ plugins: [
232
+ watchClientPlugin(saved),
233
+ pluginIfelse(ifPluginVars),
234
+ obfuscatePlugin(saved.isObfuscate, entryPoints, []),
235
+ ],
236
+ });
237
+
238
+ isDev && (await ctx.watch());
239
+ isDev && console.log(`[dev-client:${saved.appName}] Watching...`);
240
+ };
241
+
242
+ const watchApiPlugin = (isDev, httpPort) => {
243
+ return {
244
+ name: 'watchApiPlugin',
245
+ setup(build) {
246
+ build.onEnd(async (res) => {
247
+ console.log(`[dev-api] Build finished`);
248
+
249
+ if (isDev) {
250
+ await triggerRefreshServer(isDev, httpPort);
251
+ }
252
+ });
253
+ },
254
+ };
255
+ };
256
+
257
+ const watchApi = async (saved, isDev, entryPoints) => {
258
+ const cmd = isDev ? esbuild.context : esbuild.build;
259
+ const ctx = await cmd({
260
+ entryPoints,
261
+ outdir: saved.outdirApi,
262
+ // outbase,
263
+ platform: 'node',
264
+ sourcemap: !!isDev, // inline
265
+ format: 'cjs', // iife, cjs
266
+ bundle: true,
267
+ treeShaking: true,
268
+ metafile: true,
269
+ external: ['better-sqlite3', 'nodemailer', 'pdfkit', 'sharp'],
270
+ minify: !isDev,
271
+ plugins: [watchApiPlugin(isDev, saved.httpPort)],
272
+ });
273
+
274
+ isDev && (await ctx.watch());
275
+ isDev && console.log(`[dev-api:${saved.appName}] Watching...`);
276
+ };
277
+
278
+ const watchMarkdownPlugin = (saved) => {
279
+ return {
280
+ name: 'watchMarkdownPlugin',
281
+ setup(build) {
282
+ build.onEnd(async (res) => {
283
+ // console.log(`Build meta data: `, res);
284
+ console.log(`[dev-markdown:${saved.appName}] Build finished`);
285
+ await markdownProcessOnEnd(saved.markdownEntryPoints.indir, saved.markdownEntryPoints.outdir);
286
+ });
287
+ },
288
+ };
289
+ };
290
+
291
+ const watchMarkdown = async (saved, entryPoints) => {
292
+ const cmd = saved.isDev ? esbuild.context : esbuild.build;
293
+ const ctx = await cmd({
294
+ entryPoints: [`${entryPoints.indir}/**/*.md`],
295
+ outdir: entryPoints.outdir,
296
+ write: false,
297
+ loader: { '.md': 'empty' },
298
+ plugins: [watchMarkdownPlugin({ ...saved, markdownEntryPoints: entryPoints })],
299
+ });
300
+
301
+ saved.isDev && (await ctx.watch());
302
+ };
303
+
304
+ const watchAdditionalFiles = async (saved, entryPoints) => {
305
+ const cmd = saved.isDev ? esbuild.context : esbuild.build;
306
+ const ctx = await cmd({
307
+ entryPoints: entryPoints,
308
+ outdir: 'dist/tmp',
309
+ write: false,
310
+ loader: { '.html': 'empty', '.css': 'empty', '.js': 'empty', '.md': 'empty' },
311
+ plugins: [watchClientPlugin(saved)],
312
+ });
313
+
314
+ saved.isDev && (await ctx.watch());
315
+ };
316
+
317
+ // Add Server, API, Client and additional files entry points to esbuild
318
+ const start = async () => {
319
+ // Any code accessing process.env should be after loadEnv
320
+ // process env files, "#!import .env" in .env file can be used to include another env file
321
+ let envFile = process.argv.find((i) => i.startsWith('--env='))?.substring(6);
322
+ // load env file, but don't overwrite existing env variables
323
+ await loadEnv(envFile);
324
+
325
+ const serverRootPathEnv = process.env['SERVER_ROOT_PATH'];
326
+ if (!serverRootPathEnv) {
327
+ console.error('SERVER_ROOT_PATH is not set');
328
+ return 0;
329
+ }
330
+ const serverRootPath = path.resolve(serverRootPathEnv);
331
+ await fs.mkdir(serverRootPath, { recursive: true });
332
+ if (!(await pathExists(serverRootPath))) {
333
+ console.error(`Can't create server root path: ${serverRootPath}`);
334
+ return 0;
335
+ }
336
+
337
+ // command to start the server when source files are changed
338
+ const npmCmd = process.argv.find((i) => i.startsWith('--cmd='))?.substring(6);
339
+ const isDev = process.argv.find((i) => i === '--dev=1');
340
+ const isMobile = process.argv.find((i) => i === '--mobile=1'); // when this changed, need to rebuild index.html
341
+ const isObfuscate = !isDev && process.argv.find((i) => i === '--obfuscate=1');
342
+ // this is for esbuild conditional compile
343
+ ifPluginVars.DEV = isDev ? '1' : '0';
344
+ ifPluginVars.MOBILE = isMobile ? '1' : '0';
345
+
346
+ // All apps should be defined in .env file like this:
347
+ // APPS=domain1.com,domain2.com
348
+ const apps = (process.env['APPS'] || '').split(',');
349
+
350
+ const httpPort = process.env['HTTP_PORT'];
351
+ for (const app of apps) {
352
+ const appJson = `apps/${app}/lupine.json`;
353
+ const additionalFiles = [];
354
+ const appCfg = await readJson(appJson);
355
+ const appName = appCfg['name'];
356
+ const appDir = path.dirname(appJson);
357
+ if (appCfg['watchFiles']) {
358
+ appCfg['watchFiles'].forEach((item) => additionalFiles.push(`${appDir}/${item}`));
359
+ }
360
+
361
+ // create data folder
362
+ await fs.mkdir(`${serverRootPath}/${appName}_data`, { recursive: true });
363
+
364
+ const saved = {
365
+ isDev,
366
+ isMobile,
367
+ isObfuscate,
368
+ defaultThemeName: 'light',
369
+ appName,
370
+ httpPort,
371
+ serverRoot: serverRootPath,
372
+ outdirWeb: `${serverRootPath}/${appName}_web`,
373
+ outdirApi: `${serverRootPath}/${appName}_api`,
374
+ outdirData: `${serverRootPath}/${appName}_data`,
375
+ copyFiles: appCfg['copyFiles'].map((item) => ({
376
+ from: `${appDir}/${item.from}`,
377
+ to:
378
+ item.type == 'data'
379
+ ? `${serverRootPath}/${appName}_data/${item.to}`
380
+ : `${serverRootPath}/${appName}_web/${item.to}`,
381
+ })),
382
+ };
383
+
384
+ appCfg['webEntryPoints'].forEach((item, index) => {
385
+ const entryPoint = `${appDir}/${item.index}`;
386
+ const indexHtml = `${appDir}/${item.html}`;
387
+ watchClient(
388
+ {
389
+ ...saved,
390
+ outdirSub: item.outdir,
391
+ outdir: `${serverRootPath}/${appName}_web/` + item.outdir,
392
+ indexHtml,
393
+ copyFiles: [],
394
+ },
395
+ isDev,
396
+ [entryPoint],
397
+ path.dirname(entryPoint)
398
+ );
399
+ additionalFiles.push(indexHtml);
400
+ });
401
+
402
+ if (appCfg['apiEntryPoint']) {
403
+ const entryPointApi = `${appDir}/${appCfg['apiEntryPoint']}`;
404
+ watchApi(saved, isDev, [entryPointApi]);
405
+ }
406
+ if (appCfg['markdownEntryPoints']) {
407
+ const markdownEntryPoints = {
408
+ indir: `${appDir}/${appCfg['markdownEntryPoints'].indir}`,
409
+ outdir: `${appDir}/${appCfg['markdownEntryPoints'].outdir}`,
410
+ };
411
+ watchMarkdown(saved, markdownEntryPoints);
412
+ }
413
+ if (additionalFiles.length > 0) {
414
+ // when some resources are changed, need to run command or refresh the server
415
+ watchAdditionalFiles(saved, additionalFiles);
416
+ }
417
+ }
418
+
419
+ watchServer(isDev, npmCmd, httpPort, serverRootPath);
420
+ watchAppLoader(isDev, npmCmd, httpPort, serverRootPath);
421
+ };
422
+ start();
@@ -0,0 +1,6 @@
1
+ {
2
+ "WEB.pageLimit": "15",
3
+ "WEB.siteTitle": "Sample title",
4
+ "WEB.siteEmail": "sample@sample.com",
5
+ "WEB.siteUrl": "https://sample.com"
6
+ }
@@ -0,0 +1,4 @@
1
+ #-- sql queries to create tables
2
+ #-- Never name a table with plural (such as "users")
3
+ #-- Never name a table using a reserved keyword (such as "user")
4
+ #-- Never prefix your table name with "tbl" or some other object type prefix
@@ -0,0 +1,15 @@
1
+ import { IApiBase, ApiRouter, StaticServer, ApiModule } from 'lupine.api';
2
+
3
+ class DocApi implements IApiBase {
4
+ protected router = new ApiRouter();
5
+
6
+ constructor() {
7
+ this.router.use('*', new StaticServer().processRequest.bind(new StaticServer()));
8
+ }
9
+
10
+ public getRouter(): ApiRouter {
11
+ return this.router;
12
+ }
13
+ }
14
+
15
+ export const apiModule = new ApiModule(new DocApi());
@@ -0,0 +1,6 @@
1
+ {
2
+ "WEB.pageLimit": "15",
3
+ "WEB.siteTitle": "Sample title",
4
+ "WEB.siteEmail": "sample@sample.com",
5
+ "WEB.siteUrl": "https://sample.com"
6
+ }
@@ -0,0 +1,4 @@
1
+ #-- sql queries to create tables
2
+ #-- Never name a table with plural (such as "users")
3
+ #-- Never name a table using a reserved keyword (such as "user")
4
+ #-- Never prefix your table name with "tbl" or some other object type prefix
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "doc-starter",
3
+ "markdownEntryPoints": {
4
+ "indir": "web/markdown",
5
+ "outdir": "web/src/markdown-built"
6
+ },
7
+ "webEntryPoints": [
8
+ {
9
+ "index": "web/src/index.tsx",
10
+ "html": "web/src/index.html",
11
+ "outdir": "/"
12
+ },
13
+ {
14
+ "index": "web/github-pj-name/index.tsx",
15
+ "html": "web/github-pj-name/index.html",
16
+ "outdir": "/github-pj-name"
17
+ }
18
+ ],
19
+ "apiEntryPoint": "api/src/index.ts",
20
+ "watchFiles": ["web/markdown/**/*.md"],
21
+ "copyFiles": [
22
+ {
23
+ "type": "web",
24
+ "from": "web/assets",
25
+ "to": "assets"
26
+ },
27
+ {
28
+ "type": "data",
29
+ "from": "api/resources",
30
+ "to": "resources"
31
+ }
32
+ ]
33
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "doc-starter",
3
+ "version": "1.0.0",
4
+ "description": "Documentation starter demo project",
5
+ "private": true,
6
+ "type": "module",
7
+ "scripts": {
8
+ "build": "echo 'run build at project root directory'"
9
+ },
10
+ "dependencies": {
11
+ "lupine.press": "^1.0.1"
12
+ }
13
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "",
3
+ "short_name": "",
4
+ "icons": [
5
+ {
6
+ "src": "/assets/android-chrome-192x192.png",
7
+ "sizes": "192x192",
8
+ "type": "image/png"
9
+ }
10
+ ],
11
+ "theme_color": "#FFFFFF",
12
+ "background_color": "#FFFFFF",
13
+ "display": "standalone"
14
+ }
@@ -0,0 +1,21 @@
1
+ <!DOCTYPE html>
2
+ <html data-theme="<!--META-THEME-->">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <link rel="manifest" href="{SUBDIR}/assets/site.webmanifest" />
6
+ <title><!--META-TITLE--></title>
7
+ <link rel="apple-touch-icon" sizes="180x180" href="{SUBDIR}/assets/apple-touch-icon.png" />
8
+ <link rel="icon" type="image/png" sizes="32x32" href="{SUBDIR}/assets/favicon-32x32.png" />
9
+ <link rel="icon" type="image/png" sizes="16x16" href="{SUBDIR}/assets/favicon-16x16.png" />
10
+ <link rel="icon" type="image/png" sizes="192x192" href="{SUBDIR}/assets/android-chrome-192x192.png" />
11
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
12
+ <!--META-ENV-START-->
13
+ <!--META-ENV-END-->
14
+ <link rel="shortcut icon" href="{SUBDIR}/assets/favicon.ico?t={hash}" />
15
+ <link rel="stylesheet" type="text/css" href="{SUBDIR}/index.css?t={hash}" />
16
+ <script defer src="{SUBDIR}/index.js#t={hash}"></script>
17
+ </head>
18
+ <body>
19
+ <div class="lupine-root"></div>
20
+ </body>
21
+ </html>
@@ -0,0 +1,35 @@
1
+ import {
2
+ bindRouter,
3
+ PageRouter,
4
+ bindTheme,
5
+ bindLang,
6
+ setDefaultPageTitle,
7
+ isFrontEnd,
8
+ debugWatch,
9
+ webEnv,
10
+ setDefaultMetaDescription,
11
+ bindGlobalStyle,
12
+ } from 'lupine.components';
13
+ import { bindPressData, PressPage, pressThemes, setPressSubDir } from 'lupine.press';
14
+ import { ClientEnvKeys } from '../src/client-env-keys';
15
+ import { baseCss } from '../src/styles/base-css';
16
+ import { markdownConfig } from '../src/markdown-built/markdown-config';
17
+
18
+ if (isFrontEnd() && webEnv(ClientEnvKeys.NODE_ENV, '') === 'development') {
19
+ debugWatch(webEnv(ClientEnvKeys.API_PORT, 0));
20
+ }
21
+
22
+ bindLang('en', {});
23
+ bindTheme('light', pressThemes);
24
+ bindGlobalStyle('comm-css', baseCss, false, true);
25
+ setDefaultPageTitle('Doc Starter');
26
+ setDefaultMetaDescription('Doc Starter Demo');
27
+
28
+ bindPressData(markdownConfig);
29
+ setPressSubDir('/github-pj-name');
30
+
31
+ const pageRouter = new PageRouter();
32
+ pageRouter.setSubDir('/github-pj-name');
33
+ pageRouter.use('*', PressPage);
34
+
35
+ bindRouter(pageRouter);
@@ -0,0 +1,6 @@
1
+ ---
2
+ title: Core Essentials
3
+ sidebar:
4
+ - items:
5
+ - /en/essentials/list
6
+ ---
@@ -0,0 +1,18 @@
1
+ ---
2
+ title: Essentials List
3
+ ---
4
+
5
+ > [!NOTE]
6
+ > This is a demo document.
7
+
8
+ # Essentials List
9
+
10
+ This is a simplified essentials list doc for demo purposes.
11
+
12
+ ## Feature Overview
13
+
14
+ Here is an example listing key features:
15
+
16
+ ```tsx
17
+ const features = ['Fast', 'Reliable', 'Easy to use'];
18
+ ```
@@ -0,0 +1,18 @@
1
+ ---
2
+ title: Installation
3
+ ---
4
+
5
+ > [!NOTE]
6
+ > This is a demo document.
7
+
8
+ # Installation
9
+
10
+ This is a simplified installation guide for demo purposes.
11
+
12
+ ## Basic Install
13
+
14
+ Run the following command:
15
+
16
+ ```bash
17
+ npm install my-project
18
+ ```
@@ -0,0 +1,22 @@
1
+ ---
2
+ title: Getting Started
3
+ ---
4
+
5
+ > [!NOTE]
6
+ > This is a demo document.
7
+
8
+ # Getting Started
9
+
10
+ This is a simplified getting started guide for demo purposes.
11
+
12
+ ## Quick Start
13
+
14
+ Initialize your project:
15
+
16
+ ```javascript
17
+ import { init } from 'my-project';
18
+
19
+ init({
20
+ debug: true,
21
+ });
22
+ ```