sugar-scripts 0.2.0-beta.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.
@@ -0,0 +1,27 @@
1
+ import {
2
+ findProject,
3
+ findPackage
4
+ } from '../shared/file-helpers';
5
+
6
+ import {
7
+ SugarScriptsContext
8
+ } from './running-context';
9
+
10
+ export const initRunningContext = async (dir: string) => {
11
+ const {
12
+ root,
13
+ packageJson,
14
+ packageConfig
15
+ } = await findPackage(dir);
16
+ const { projectRoot, projectConfig } = await findProject(root);
17
+ if (!packageConfig || !projectConfig) {
18
+ throw new Error('')
19
+ }
20
+ return new SugarScriptsContext({
21
+ root,
22
+ packageName: packageJson.name,
23
+ packageConfig,
24
+ projectRoot,
25
+ projectConfig
26
+ })
27
+ }
@@ -0,0 +1,12 @@
1
+ import {
2
+ Application
3
+ } from 'sugar-server';
4
+
5
+ export const runApplication = (
6
+ ApplicationClass: typeof Application,
7
+ port: number
8
+ ) => {
9
+ const app = new ApplicationClass();
10
+
11
+ app.listen(port)
12
+ }
@@ -0,0 +1,92 @@
1
+ import { createHash } from 'crypto';
2
+ import path from 'path';
3
+
4
+ type BuildEntry = { [key: string]: string|string[] };
5
+
6
+ interface PackageConfig {
7
+ browser?: {
8
+ /**
9
+ * 是否构建dll用的输出
10
+ */
11
+ dll?: boolean;
12
+ /**
13
+ * 输出目录
14
+ */
15
+ output: string;
16
+ /**
17
+ * controller/index
18
+ */
19
+ input?: string;
20
+ entry?: BuildEntry
21
+ };
22
+
23
+ server?: {
24
+ dll?: boolean;
25
+ output: string;
26
+ entry: string;
27
+ render?: string;
28
+ };
29
+ }
30
+
31
+ interface ProjectConfig {
32
+ cacheDir: string;
33
+ }
34
+
35
+ export function getHashFromRoot (
36
+ root: string
37
+ ) {
38
+ const hash = createHash('md5');
39
+ hash.update(root);
40
+ return `s_${hash.digest('hex')}`;
41
+ }
42
+
43
+ export class SugarScriptsContext {
44
+ root: string;
45
+ rootHash: string;
46
+ packageName: string;
47
+ packageConfig: PackageConfig;
48
+ projectRoot: string;
49
+ projectConfig: ProjectConfig;
50
+
51
+ constructor (
52
+ {
53
+ root,
54
+ packageName,
55
+ packageConfig,
56
+ projectRoot,
57
+ projectConfig
58
+ }: {
59
+ root: string;
60
+ packageName: string;
61
+ packageConfig: PackageConfig;
62
+ projectRoot: string;
63
+ projectConfig: ProjectConfig;
64
+ }
65
+ ) {
66
+ this.root = root;
67
+ this.packageName = packageName;
68
+ this.packageConfig = packageConfig;
69
+ this.projectRoot = projectRoot;
70
+ this.projectConfig = projectConfig;
71
+ this.rootHash = getHashFromRoot(root);
72
+ }
73
+
74
+ serverEntryName = 'main';
75
+
76
+ getStartFilePath () {
77
+ if (this.packageConfig.server) {
78
+ return path.resolve(
79
+ this.root,
80
+ this.packageConfig.server.output,
81
+ this.serverEntryName
82
+ )
83
+ }
84
+ }
85
+
86
+ getCacheDir () {
87
+ return path.resolve(
88
+ this.projectRoot,
89
+ this.projectConfig.cacheDir
90
+ )
91
+ }
92
+ }
@@ -0,0 +1,66 @@
1
+ import type {
2
+ ControllerContext
3
+ } from 'sugar-server';
4
+ import type WebpackChainConfig from 'webpack-chain';
5
+
6
+ import {
7
+ SugarScriptsContext
8
+ } from './core/running-context'
9
+
10
+ export namespace SugarScriptsProject {
11
+ export type BrowserWebpackConfig = CustomWebpackConfig;
12
+
13
+ export type ServerWebpackConfig = CustomWebpackConfig;
14
+
15
+ export interface PackageConfig {
16
+ browser?: {
17
+ /**
18
+ * 是否构建dll用的输出
19
+ */
20
+ dll?: boolean;
21
+ /**
22
+ * 输出目录
23
+ */
24
+ output: string;
25
+ /**
26
+ * controller/index
27
+ */
28
+ input?: string;
29
+ entry?: BuildEntry
30
+ };
31
+
32
+ server?: {
33
+ dll?: boolean;
34
+ output: string;
35
+ entry: string;
36
+ render?: string;
37
+ };
38
+ }
39
+
40
+ export interface ProjectConfig {
41
+ cacheDir: string;
42
+ }
43
+
44
+
45
+ export interface CustomRender<C = any> {
46
+ (
47
+ ctx: ControllerContext,
48
+ entries: string[] | {[key: string]: string[]},
49
+ custom: C
50
+ ): string
51
+ };
52
+ }
53
+
54
+ type BuildEntry = { [key: string]: string|string[] };
55
+
56
+ export type CustomWebpackConfig = (
57
+ webpackChainConfig: WebpackChainConfig,
58
+ context: SugarScriptsContext
59
+ ) => void;
60
+
61
+ export type CustomRender<C = any> = (
62
+ ctx: ControllerContext,
63
+ entries: string[] | {[key: string]: string[]},
64
+ custom: C
65
+ ) => string;
66
+
package/src/index.ts ADDED
@@ -0,0 +1,7 @@
1
+ export * as entry from './core/entry';
2
+ export * as appUtils from './app-utils'
3
+
4
+ export {
5
+ SugarScriptsProject
6
+ } from './custom-config.type';
7
+
@@ -0,0 +1,192 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+
4
+ import {
5
+ SUGAR_PACKAGE_CONFIG_FILENAME,
6
+ SUGAR_PROJECT_CONFIG_FILENAME
7
+ } from '../constants';
8
+ import {
9
+ SugarScriptsProject
10
+ } from '../custom-config.type';
11
+ // nodejs v10 支持
12
+ // nodejs v14 才开始支持require('fs/promises')
13
+ const fsPromises = fs.promises;
14
+
15
+ export const searchFiles = async (
16
+ dir: string,
17
+ fileCheck: string | ((filePath: string) => boolean),
18
+ exclude?: string
19
+ ) => {
20
+ try {
21
+ await fsPromises.access(dir)
22
+ } catch (e) {
23
+ return [];
24
+ }
25
+ const files = await fsPromises.readdir(dir);
26
+ let currentFiles: string[] = [];
27
+
28
+ await Promise.all(
29
+ files.map(async (filename) => {
30
+ if (exclude === filename) return;
31
+ const fileOrDir = path.join(dir, filename);
32
+ const stats = await fsPromises.stat(fileOrDir);
33
+ if (stats.isFile()) {
34
+ if (typeof fileCheck === 'string') {
35
+ if (filename === fileCheck) {
36
+ currentFiles.push(fileOrDir)
37
+ }
38
+ } else if (fileCheck(fileOrDir)) {
39
+ currentFiles.push(fileOrDir)
40
+ }
41
+ } else if (stats.isDirectory()) {
42
+ const childCurrentFiles = await searchFiles(fileOrDir, fileCheck);
43
+ currentFiles = currentFiles.concat(childCurrentFiles);
44
+ }
45
+ })
46
+ )
47
+ return currentFiles;
48
+ }
49
+
50
+ export const findSiblingFile = async (
51
+ currentFilePath: string,
52
+ searchFileName: string
53
+ ) => {
54
+ const dir = path.dirname(currentFilePath);
55
+ const searchFilePath = path.join(dir, searchFileName);
56
+
57
+ const searchFileStat = await fsPromises.stat(searchFilePath);
58
+ if (searchFileStat.isFile()) {
59
+ return searchFilePath;
60
+ }
61
+
62
+ return null;
63
+ }
64
+
65
+ export const findParentFile = async (
66
+ currentFilePath: string,
67
+ searchFileName: string
68
+ ) => {
69
+ const dir = path.resolve(
70
+ path.dirname(currentFilePath),
71
+ '../'
72
+ );
73
+ const searchFilePath = path.join(dir, searchFileName);
74
+
75
+ const searchFileStat = await fsPromises.stat(searchFilePath);
76
+ if (searchFileStat.isFile()) {
77
+ return searchFilePath;
78
+ }
79
+
80
+ return null;
81
+ }
82
+
83
+ export const loadJSON = async (filePath: string) => {
84
+ try {
85
+ const contentBuffer = await fsPromises.readFile(filePath);
86
+ const content = contentBuffer.toString();
87
+
88
+ return JSON.parse(content);
89
+ } catch (e) {
90
+ return {};
91
+ }
92
+ }
93
+
94
+ export const writeFileSync = (
95
+ filePath: string,
96
+ data: string | NodeJS.ArrayBufferView
97
+ ) => {
98
+ const dir = path.dirname(filePath);
99
+ fs.mkdirSync(dir, { recursive: true })
100
+ fs.writeFileSync(
101
+ filePath,
102
+ data
103
+ )
104
+ }
105
+
106
+ export const findPackage = async (dir: string): Promise<{
107
+ root: string;
108
+ packageJson: any;
109
+ packageConfig: SugarScriptsProject.PackageConfig | null;
110
+ }> => {
111
+ const packagePath = path.resolve(dir, 'package.json');
112
+
113
+ let existed = true;
114
+ try {
115
+ await fsPromises.access(packagePath, fs.constants.F_OK);
116
+ } catch (e) {
117
+ existed = false;
118
+ }
119
+
120
+ let packageConfig = null;
121
+ try {
122
+ packageConfig = require(
123
+ path.resolve(dir, SUGAR_PACKAGE_CONFIG_FILENAME)
124
+ ).packageConfig;
125
+ } catch (e) {}
126
+
127
+ if (existed) {
128
+ return {
129
+ root: dir,
130
+ packageJson: require(
131
+ packagePath
132
+ ),
133
+ packageConfig
134
+ };
135
+ }
136
+
137
+ if (dir === '/') {
138
+ throw new Error('not found package.json');
139
+ }
140
+
141
+ return findPackage(
142
+ path.resolve(dir, '../')
143
+ )
144
+ }
145
+
146
+ export const findProject = async (dir: string): Promise<{
147
+ projectRoot: string,
148
+ projectConfig: SugarScriptsProject.ProjectConfig
149
+ }> => {
150
+ const projectPath = path.resolve(dir, SUGAR_PROJECT_CONFIG_FILENAME);
151
+
152
+ let projectConfig = null;
153
+ try {
154
+ projectConfig = require(projectPath).projectConfig;
155
+ } catch (e) {}
156
+
157
+ if (dir === '/') {
158
+ throw new Error('not found sugar.project');
159
+ }
160
+
161
+ if (projectConfig) {
162
+ return {
163
+ projectRoot: dir,
164
+ projectConfig
165
+ };
166
+ }
167
+
168
+ return findProject(
169
+ path.resolve(dir, '../')
170
+ )
171
+ }
172
+
173
+ export const rm = async (filePath: string) => {
174
+ const stat = await fsPromises.stat(filePath);
175
+ if (
176
+ stat.isFile()
177
+ || stat.isSymbolicLink()
178
+ ) {
179
+ await fsPromises.unlink(filePath);
180
+ return;
181
+ }
182
+ if (stat.isDirectory()) {
183
+ const childFilePaths = await fsPromises.readdir(filePath);
184
+ await Promise.all(
185
+ childFilePaths.map((childFilePath) => (
186
+ rm(path.resolve(filePath, childFilePath))
187
+ ))
188
+ );
189
+ await fsPromises.rmdir(filePath);
190
+ return;
191
+ }
192
+ }
@@ -0,0 +1,8 @@
1
+ declare module 'babel-loader' {}
2
+
3
+ declare module 'webpack-manifest-plugin' {
4
+ export class WebpackManifestPlugin {
5
+ constructor (options: any);
6
+ apply: any
7
+ }
8
+ }
@@ -0,0 +1,5 @@
1
+ var a = require(process.env.SUGAR_PROJECT_REAL_ENTRY || '');
2
+
3
+ a.default.ENTRIES = process.env.SUGAR_PROJECT_ENTRIES;
4
+
5
+ export default a.default;
@@ -0,0 +1,115 @@
1
+ import webpack from 'webpack';
2
+
3
+ import {
4
+ writeFileSync
5
+ } from '../shared/file-helpers';
6
+
7
+ export interface DllDependenciesManifestPluginOptions {
8
+ dllAssets: {
9
+ [dllName: string]: string[];
10
+ }
11
+ fileName: string,
12
+ generate: (entries: {
13
+ [entry: string]: string[]
14
+ }) => any
15
+ }
16
+
17
+ const PLUGIN_NAME = 'DllDependenciesManifestPlugin';
18
+
19
+ export class DllDependenciesManifestPlugin {
20
+ options: DllDependenciesManifestPluginOptions;
21
+ constructor (options: DllDependenciesManifestPluginOptions) {
22
+ this.options = options;
23
+ }
24
+
25
+ apply (compiler: webpack.Compiler) {
26
+ const logger = compiler.getInfrastructureLogger(PLUGIN_NAME);
27
+
28
+ compiler.hooks.done.tap(
29
+ PLUGIN_NAME,
30
+ (stats) => {
31
+ var data = stats.toJson({
32
+ all: false,
33
+ entrypoints: true,
34
+ chunks: true,
35
+ chunkModules: true,
36
+ // nestedModules: true,
37
+ dependentModules: true,
38
+ moduleAssets: true,
39
+ assets: true,
40
+ modules: true,
41
+ // moduleTrace: true,
42
+ // reasons: true,
43
+ chunkRelations: true,
44
+ ids: true,
45
+ publicPath: true
46
+ })
47
+
48
+ const publicPath = data.publicPath;
49
+ const entries: {
50
+ [entry: string]: string[]
51
+ } = data.entrypoints
52
+ ? Object.keys(data.entrypoints).reduce((entries, entryKey) => {
53
+ entries[entryKey] = [];
54
+ const entryAssets = data.entrypoints![entryKey].assets;
55
+ if (entryAssets) {
56
+ const normalizePath = (path: string): string => {
57
+ if (!path.endsWith('/')) {
58
+ return `${path}/`;
59
+ }
60
+
61
+ return path;
62
+ };
63
+
64
+ entries[entryKey] = entries[entryKey].concat(
65
+ entryAssets.map(({ name }) => (
66
+ publicPath ? normalizePath(publicPath) + name : name
67
+ ))
68
+ )
69
+ };
70
+ return entries;
71
+ }, {} as {
72
+ [entry: string]: string[]
73
+ })
74
+ : {};
75
+
76
+ data.chunks?.forEach((chunk) => {
77
+ if (chunk.entry) {
78
+ let depDllAssets: string[] = [];
79
+ chunk.modules?.forEach((module) => {
80
+ if (
81
+ module.type !== 'module'
82
+ || !module.identifier
83
+ ) {
84
+ return;
85
+ }
86
+ const dllNameResult = /dll-reference (.+)$/.exec(module.identifier)
87
+ if (!dllNameResult) {
88
+ return;
89
+ }
90
+ const dllName = dllNameResult[1];
91
+ depDllAssets = depDllAssets.concat(
92
+ this.options.dllAssets[dllName]
93
+ )
94
+ })
95
+ if (entries[chunk.id as any]) {
96
+ entries[chunk.id as any] = depDllAssets.concat(entries[chunk.id as any])
97
+ }
98
+ }
99
+ })
100
+
101
+ // 去重复
102
+ Object.keys(entries).forEach((key) => {
103
+ entries[key] = Array.from(new Set(entries[key]))
104
+ })
105
+
106
+ const manifest = this.options.generate(entries);
107
+
108
+ writeFileSync(
109
+ this.options.fileName,
110
+ JSON.stringify(manifest, null, 2)
111
+ )
112
+ }
113
+ )
114
+ }
115
+ }
@@ -0,0 +1,73 @@
1
+ import path from 'path';
2
+
3
+ import {
4
+ searchFiles,
5
+ findParentFile,
6
+ loadJSON
7
+ } from '../shared/file-helpers';
8
+
9
+ export async function loadAllDllModulesManifest (
10
+ dir: string,
11
+ exclude?: string
12
+ ): Promise<{
13
+ context: string;
14
+ manifest: {
15
+ name: string;
16
+ content: any;
17
+ };
18
+ moduleName: string;
19
+ name: string;
20
+ assets: string[]
21
+ }[]> {
22
+ const moduleFilePaths = await searchFiles(
23
+ dir,
24
+ 'dll.modules.manifest.json',
25
+ exclude
26
+ );
27
+
28
+ const modules = await Promise.all(
29
+ moduleFilePaths.map(async (moduleFilePath) => {
30
+ const moduleConfigFilePath = await findParentFile(
31
+ moduleFilePath,
32
+ 'manifest.json'
33
+ );
34
+ if (!moduleConfigFilePath) {
35
+ throw new Error(`not find manifest.json for ${moduleFilePath}`)
36
+ }
37
+ const moduleConfig = await loadJSON(moduleConfigFilePath);
38
+ const module = await loadJSON(moduleFilePath);
39
+ const name = module.name.split('_sn_').slice(1).join('_sn_');
40
+ return {
41
+ context: moduleConfig.context,
42
+ manifest: module,
43
+ moduleName: module.name,
44
+ name,
45
+ assets: moduleConfig.entries[name]
46
+ }
47
+ })
48
+ )
49
+
50
+ return modules.filter((module) => !!module) as {
51
+ context: string;
52
+ manifest: {
53
+ name: string;
54
+ content: any;
55
+ };
56
+ moduleName: string;
57
+ name: string;
58
+ assets: string[]
59
+ }[];
60
+ }
61
+
62
+ export async function loadBaseManifest (
63
+ dir: string,
64
+ rootHash: string
65
+ ) {
66
+ return loadJSON(
67
+ path.resolve(
68
+ dir,
69
+ rootHash,
70
+ './manifest.json'
71
+ )
72
+ );
73
+ }
@@ -0,0 +1,22 @@
1
+ import webpack from 'webpack';
2
+ import WebpackChainConfig from 'webpack-chain';
3
+
4
+
5
+ export const runWebpack = (
6
+ chainConfig: WebpackChainConfig
7
+ ) => {
8
+ const compiler = webpack(
9
+ chainConfig.toConfig()
10
+ );
11
+
12
+ return new Promise((resolve, reject) => {
13
+ compiler.run((err, stats) => {
14
+ if (err || stats && stats.hasErrors()) {
15
+ reject(err || stats?.toString())
16
+ return;
17
+ }
18
+ console.log(stats?.toString())
19
+ resolve(stats)
20
+ })
21
+ })
22
+ }