@modern-js/core 1.2.1-rc.0 → 1.4.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 (53) hide show
  1. package/CHANGELOG.md +57 -5
  2. package/dist/js/modern/cli.js +29 -0
  3. package/dist/js/modern/config/index.js +11 -5
  4. package/dist/js/modern/context.js +28 -16
  5. package/dist/js/modern/index.js +27 -12
  6. package/dist/js/modern/initWatcher.js +19 -3
  7. package/dist/js/modern/loadPlugins.js +21 -6
  8. package/dist/js/node/cli.js +35 -0
  9. package/dist/js/node/config/index.js +11 -5
  10. package/dist/js/node/context.js +28 -16
  11. package/dist/js/node/index.js +36 -39
  12. package/dist/js/node/initWatcher.js +18 -2
  13. package/dist/js/node/loadPlugins.js +22 -5
  14. package/dist/types/cli.d.ts +1 -0
  15. package/dist/types/config/index.d.ts +20 -14
  16. package/dist/types/context.d.ts +6 -1
  17. package/dist/types/index.d.ts +22 -7
  18. package/dist/types/initWatcher.d.ts +2 -1
  19. package/dist/types/loadPlugins.d.ts +8 -1
  20. package/jest.config.js +8 -0
  21. package/modern.config.js +0 -7
  22. package/package.json +17 -10
  23. package/tests/btsm.test.ts +20 -0
  24. package/tests/config.test.ts +137 -0
  25. package/tests/context.test.ts +68 -0
  26. package/tests/fixtures/index-test/package.json +3 -0
  27. package/tests/index.test.ts +74 -0
  28. package/tests/initWatcher.test.ts +63 -0
  29. package/tests/loadEnv.test.ts +1 -1
  30. package/tests/loadPlugin.test.ts +36 -2
  31. package/tests/mergeConfig.test.ts +1 -1
  32. package/tests/repeatKeyWarning.test.ts +2 -2
  33. package/tests/schema.test.ts +1 -1
  34. package/tests/tsconfig.json +1 -3
  35. package/tests/utils.test.ts +8 -0
  36. package/tsconfig.json +1 -3
  37. package/src/config/defaults.ts +0 -101
  38. package/src/config/index.ts +0 -297
  39. package/src/config/mergeConfig.ts +0 -69
  40. package/src/config/schema/deploy.ts +0 -17
  41. package/src/config/schema/index.ts +0 -116
  42. package/src/config/schema/output.ts +0 -65
  43. package/src/config/schema/server.ts +0 -106
  44. package/src/config/schema/source.ts +0 -34
  45. package/src/config/schema/tools.ts +0 -15
  46. package/src/context.ts +0 -46
  47. package/src/index.ts +0 -277
  48. package/src/initWatcher.ts +0 -77
  49. package/src/loadEnv.ts +0 -23
  50. package/src/loadPlugins.ts +0 -91
  51. package/src/types.d.ts +0 -0
  52. package/src/utils/commander.ts +0 -22
  53. package/src/utils/repeatKeyWarning.ts +0 -29
@@ -0,0 +1,137 @@
1
+ import path from 'path';
2
+ // import os from 'os';
3
+ import { isDev, getPort } from '@modern-js/utils';
4
+ import { resolveConfig } from '../src/config';
5
+ import {
6
+ cli,
7
+ loadUserConfig,
8
+ initAppContext,
9
+ initAppDir,
10
+ manager,
11
+ createPlugin,
12
+ registerHook,
13
+ useRunner,
14
+ } from '../src';
15
+ import { defaults } from '../src/config/defaults';
16
+
17
+ jest.mock('@modern-js/utils', () => ({
18
+ __esModule: true,
19
+ ...jest.requireActual('@modern-js/utils'),
20
+ isDev: jest.fn(),
21
+ getPort: jest.fn(),
22
+ }));
23
+
24
+ // const kOSRootDir =
25
+ // os.platform() === 'win32' ? process.cwd().split(path.sep)[0] : '/';
26
+
27
+ describe('config', () => {
28
+ /**
29
+ * Typescript Type annotations cannot be used for esbuild-jest
30
+ * test files that use jest.mock('@some/module')
31
+ * refer to this esbuild-jest issue:
32
+ * https://github.com/aelbore/esbuild-jest/issues/57
33
+ * TODO: find a better solution to solve this problem while allowing us
34
+ * to use esbuild, and have good TypeScript support
35
+ */
36
+ let loaded = {
37
+ config: {},
38
+ filePath: '',
39
+ dependencies: [],
40
+ pkgConfig: {},
41
+ jsConfig: {},
42
+ };
43
+ let schemas: any[] = [];
44
+ let restartWithExistingPort = 0;
45
+ let argv: string[] = ['dev'];
46
+ let configs: any[] = [];
47
+
48
+ const getResolvedConfig = async () =>
49
+ resolveConfig(loaded, configs, schemas, restartWithExistingPort, argv);
50
+
51
+ const resetParams = () => {
52
+ loaded = {
53
+ config: {},
54
+ filePath: '',
55
+ dependencies: [],
56
+ pkgConfig: {},
57
+ jsConfig: {},
58
+ };
59
+ schemas = [];
60
+ restartWithExistingPort = 0;
61
+ argv = ['dev'];
62
+ configs = [];
63
+ };
64
+ const resetMock = () => {
65
+ jest.resetAllMocks();
66
+ (isDev as jest.Mock).mockReturnValue(true);
67
+ (getPort as jest.Mock).mockReturnValue(
68
+ Promise.resolve(defaults.server.port),
69
+ );
70
+ };
71
+ beforeEach(() => {
72
+ resetParams();
73
+ resetMock();
74
+ });
75
+
76
+ it('default', () => {
77
+ expect(resolveConfig).toBeDefined();
78
+ expect(cli).toBeDefined();
79
+ expect(loadUserConfig).toBeDefined();
80
+ expect(initAppContext).toBeDefined();
81
+ expect(initAppDir).toBeDefined();
82
+ expect(manager).toBeDefined();
83
+ expect(createPlugin).toBeDefined();
84
+ expect(registerHook).toBeDefined();
85
+ expect(useRunner).toBeDefined();
86
+ });
87
+
88
+ it('initAppDir', async () => {
89
+ expect(await initAppDir(__dirname)).toBe(path.resolve(__dirname, '..'));
90
+ // expect(await initAppDir()).toBe(path.resolve(__dirname, '..'));
91
+
92
+ // FIXME: windows 下面会失败,先忽略这个测试
93
+ // try {
94
+ // await initAppDir(kOSRootDir);
95
+ // expect(true).toBe(false); // SHOULD NOT BE HERE
96
+ // } catch (err: any) {
97
+ // expect(err.message).toMatch(/no package.json found in current work dir/);
98
+ // }
99
+ });
100
+
101
+ test('should use default port if not restarting in dev mode', async () => {
102
+ let resolved = await getResolvedConfig();
103
+ expect(resolved.server.port).toEqual(defaults.server.port);
104
+ expect(getPort).toHaveBeenCalledWith(defaults.server.port);
105
+
106
+ // getResolvedConfig should use the value givin by getPort
107
+ restartWithExistingPort = -1;
108
+ (getPort as jest.Mock).mockClear();
109
+ (getPort as jest.Mock).mockReturnValue(1111);
110
+ resolved = await getResolvedConfig();
111
+ expect(resolved.server.port).toEqual(1111);
112
+ expect(getPort).toHaveBeenCalledWith(defaults.server.port);
113
+
114
+ argv = ['start'];
115
+ (isDev as jest.Mock).mockReturnValue(false);
116
+ restartWithExistingPort = 0;
117
+ resolved = await getResolvedConfig();
118
+ expect(resolved.server.port).toEqual(defaults.server.port);
119
+
120
+ restartWithExistingPort = 1234;
121
+ resolved = await getResolvedConfig();
122
+ expect(resolved.server.port).toEqual(defaults.server.port);
123
+
124
+ restartWithExistingPort = -1;
125
+ resolved = await getResolvedConfig();
126
+ expect(resolved.server.port).toEqual(defaults.server.port);
127
+ });
128
+
129
+ test('should reuse existing port if restarting in dev mode', async () => {
130
+ restartWithExistingPort = 1234;
131
+ const resolved = await getResolvedConfig();
132
+ expect(resolved.server.port).toEqual(1234);
133
+ });
134
+ });
135
+
136
+ // type TEST = Parameters<typeof resolveConfig>;
137
+ // type TypeC = TEST[1];
@@ -0,0 +1,68 @@
1
+ import path from 'path';
2
+ import { initAppContext } from '../src/context';
3
+
4
+ describe('context', () => {
5
+ it('initAppContext', () => {
6
+ const appDirectory = path.resolve(
7
+ __dirname,
8
+ './fixtures/load-plugin/user-plugins',
9
+ );
10
+ const appContext = initAppContext(appDirectory, [], false);
11
+ expect(appContext).toEqual({
12
+ appDirectory,
13
+ configFile: false,
14
+ ip: expect.any(String),
15
+ port: 0,
16
+ packageName: expect.any(String),
17
+ srcDirectory: expect.any(String),
18
+ distDirectory: expect.any(String),
19
+ sharedDirectory: expect.any(String),
20
+ nodeModulesDirectory: expect.any(String),
21
+ internalDirectory: expect.any(String),
22
+ plugins: [],
23
+ htmlTemplates: {},
24
+ serverRoutes: [],
25
+ entrypoints: [],
26
+ existSrc: true,
27
+ internalDirAlias: '@_modern_js_internal',
28
+ internalSrcAlias: '@_modern_js_src',
29
+ metaName: 'modern-js',
30
+ });
31
+ });
32
+
33
+ it('custom AppContext', () => {
34
+ const appDirectory = path.resolve(
35
+ __dirname,
36
+ './fixtures/load-plugin/user-plugins',
37
+ );
38
+
39
+ const customOptions = {
40
+ srcDir: 'source',
41
+ distDir: 'dist',
42
+ sharedDir: 'myShared',
43
+ metaName: 'jupiter',
44
+ };
45
+
46
+ const appContext = initAppContext(appDirectory, [], false, customOptions);
47
+ expect(appContext).toEqual({
48
+ appDirectory,
49
+ configFile: false,
50
+ ip: expect.any(String),
51
+ port: 0,
52
+ packageName: 'user-plugins',
53
+ srcDirectory: path.resolve(appDirectory, './source'),
54
+ distDirectory: 'dist',
55
+ sharedDirectory: path.resolve(appDirectory, './myShared'),
56
+ nodeModulesDirectory: expect.any(String),
57
+ internalDirectory: path.resolve(appDirectory, './node_modules/.jupiter'),
58
+ plugins: [],
59
+ htmlTemplates: {},
60
+ serverRoutes: [],
61
+ entrypoints: [],
62
+ existSrc: true,
63
+ internalDirAlias: '@_jupiter_internal',
64
+ internalSrcAlias: '@_jupiter_src',
65
+ metaName: 'jupiter',
66
+ });
67
+ });
68
+ });
@@ -0,0 +1,3 @@
1
+ {
2
+ "name": "index-test"
3
+ }
@@ -0,0 +1,74 @@
1
+ import path from 'path';
2
+ import { cli } from '../src';
3
+ import { resolveConfig, loadUserConfig } from '../src/config';
4
+ import { loadEnv } from '../src/loadEnv';
5
+
6
+ jest.mock('../src/config', () => ({
7
+ __esModule: true,
8
+ ...jest.requireActual('../src/config'),
9
+ loadUserConfig: jest.fn(),
10
+ resolveConfig: jest.fn(),
11
+ }));
12
+
13
+ jest.mock('../src/loadEnv', () => ({
14
+ __esModule: true,
15
+ ...jest.requireActual('../src/loadEnv'),
16
+ loadEnv: jest.fn(),
17
+ }));
18
+
19
+ describe('@modern-js/core test', () => {
20
+ let mockResolveConfig: any = {};
21
+ let mockLoadedConfig: any = {};
22
+ const cwdSpy = jest.spyOn(process, 'cwd');
23
+ const cwd = path.join(__dirname, './fixtures/index-test');
24
+
25
+ const resetMock = () => {
26
+ jest.resetAllMocks();
27
+ cwdSpy.mockReturnValue(cwd);
28
+ (resolveConfig as jest.Mock).mockReturnValue(
29
+ Promise.resolve(mockResolveConfig),
30
+ );
31
+ (loadUserConfig as jest.Mock).mockImplementation(() =>
32
+ Promise.resolve(mockLoadedConfig),
33
+ );
34
+ };
35
+
36
+ const resetValues = () => {
37
+ mockLoadedConfig = {
38
+ config: {},
39
+ filePath: false,
40
+ dependencies: [],
41
+ pkgConfig: {},
42
+ jsConfig: {},
43
+ };
44
+ mockResolveConfig = {
45
+ server: {
46
+ port: 8080,
47
+ },
48
+ output: {
49
+ path: './my/test/path',
50
+ },
51
+ };
52
+ };
53
+
54
+ beforeEach(() => {
55
+ resetValues();
56
+ resetMock();
57
+ });
58
+
59
+ it('test cli create', () => {
60
+ expect(cli).toBeTruthy();
61
+ });
62
+
63
+ it('test cli init dev', async () => {
64
+ cwdSpy.mockReturnValue(path.join(cwd, 'nested-folder'));
65
+ const options = {
66
+ beforeUsePlugins: jest.fn(),
67
+ };
68
+ options.beforeUsePlugins.mockImplementation((plugins, _) => plugins);
69
+ await cli.init(['dev'], options);
70
+ expect(loadEnv).toHaveBeenCalledWith(cwd);
71
+ expect(options.beforeUsePlugins).toHaveBeenCalledWith([], {});
72
+ // TODO: add more test cases
73
+ });
74
+ });
@@ -0,0 +1,63 @@
1
+ import * as path from 'path';
2
+ import { fs, wait } from '@modern-js/utils';
3
+ import { initWatcher } from '../src/initWatcher';
4
+
5
+ jest.useRealTimers();
6
+
7
+ const mockAppDirectory = path.join(__dirname, './fixtures/index-test');
8
+ const mockConfigDir = './config';
9
+ const mockSrcDirectory = path.join(mockAppDirectory, './src');
10
+
11
+ describe('initWatcher', () => {
12
+ afterAll(() => {
13
+ const file = path.join(mockSrcDirectory, './index.ts');
14
+ if (fs.existsSync(file)) {
15
+ fs.unlinkSync(file);
16
+ }
17
+ });
18
+
19
+ test('will trigger add event', async () => {
20
+ let triggeredType = '';
21
+ let triggeredFile = '';
22
+ const loaded = {
23
+ filePath: '',
24
+ dependencies: [],
25
+ };
26
+ const hooksRunner = {
27
+ watchFiles: async () => [mockSrcDirectory],
28
+ fileChange: jest.fn(({ filename, eventType }) => {
29
+ triggeredType = eventType;
30
+ triggeredFile = filename;
31
+ }),
32
+ };
33
+
34
+ if (await fs.pathExists(mockSrcDirectory)) {
35
+ await fs.remove(mockSrcDirectory);
36
+ }
37
+
38
+ const watcher = await initWatcher(
39
+ loaded as any,
40
+ mockAppDirectory,
41
+ mockConfigDir,
42
+ hooksRunner as any,
43
+ ['dev'],
44
+ );
45
+ await wait(100);
46
+
47
+ const file = path.join(mockSrcDirectory, './index.ts');
48
+ await fs.outputFile(file, '');
49
+ await wait(100);
50
+ // expect(hooksRunner.fileChange).toBeCalledTimes(1);
51
+ expect(triggeredType).toBe('add');
52
+ expect(file.includes(triggeredFile)).toBeTruthy();
53
+
54
+ await wait(100);
55
+ await fs.remove(file);
56
+ await wait(200);
57
+ expect(hooksRunner.fileChange).toBeCalledTimes(2);
58
+ expect(triggeredType).toBe('unlink');
59
+ expect(file.includes(triggeredFile)).toBeTruthy();
60
+
61
+ watcher?.close();
62
+ });
63
+ });
@@ -1,6 +1,6 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
- import { loadEnv } from '@/loadEnv';
3
+ import { loadEnv } from '../src/loadEnv';
4
4
 
5
5
  const fixture = path.resolve(__dirname, './fixtures/load-env');
6
6
 
@@ -1,7 +1,21 @@
1
1
  import path from 'path';
2
- import { loadPlugins } from '@/loadPlugins';
2
+ import { loadPlugins, getAppPlugins } from '../src/loadPlugins';
3
3
 
4
4
  describe('load plugins', () => {
5
+ test('getAppPlugins', () => {
6
+ const appDirectory = path.resolve(
7
+ __dirname,
8
+ './fixtures/load-plugin/user-plugins',
9
+ );
10
+ const plugins = getAppPlugins(appDirectory, ['foo' as any], {
11
+ x: {
12
+ cli: 'x',
13
+ forced: true,
14
+ } as any,
15
+ });
16
+ expect(plugins).toEqual([{ cli: 'x', forced: true }, 'foo']);
17
+ });
18
+
5
19
  test('should load user plugin successfully', () => {
6
20
  const fixture = path.resolve(
7
21
  __dirname,
@@ -23,7 +37,6 @@ describe('load plugins', () => {
23
37
  },
24
38
  {
25
39
  server: {
26
- name: 'b',
27
40
  pluginPath: path.join(fixture, './test-plugin-b.js'),
28
41
  },
29
42
  serverPath: './test-plugin-b',
@@ -31,6 +44,27 @@ describe('load plugins', () => {
31
44
  ]);
32
45
  });
33
46
 
47
+ test('should load user string plugin successfully', () => {
48
+ const fixture = path.resolve(
49
+ __dirname,
50
+ './fixtures/load-plugin/user-plugins',
51
+ );
52
+
53
+ const plugins = loadPlugins(fixture, [
54
+ path.join(fixture, './test-plugin-a.js') as any,
55
+ ]);
56
+
57
+ expect(plugins).toEqual([
58
+ {
59
+ cli: {
60
+ name: 'a',
61
+ pluginPath: path.join(fixture, './test-plugin-a.js'),
62
+ },
63
+ cliPath: path.join(fixture, './test-plugin-a.js'),
64
+ },
65
+ ]);
66
+ });
67
+
34
68
  test(`should throw error when plugin not found `, () => {
35
69
  const fixture = path.resolve(__dirname, './fixtures/load-plugin/not-found');
36
70
 
@@ -1,4 +1,4 @@
1
- import { mergeConfig } from '@/config/mergeConfig';
1
+ import { mergeConfig } from '../src/config/mergeConfig';
2
2
 
3
3
  describe('load plugins', () => {
4
4
  test('should replace property deeply', () => {
@@ -1,5 +1,5 @@
1
- import { repeatKeyWarning } from '@/utils/repeatKeyWarning';
2
- import { UserConfig } from '@/config';
1
+ import { repeatKeyWarning } from '../src/utils/repeatKeyWarning';
2
+ import { UserConfig } from '../src/config';
3
3
 
4
4
  jest.spyOn(process, 'exit').mockImplementation();
5
5
 
@@ -1,4 +1,4 @@
1
- import { patchSchema, traverseSchema } from '@/config/schema';
1
+ import { patchSchema, traverseSchema } from '../src/config/schema';
2
2
 
3
3
  describe('patch schemas', () => {
4
4
  test('should add schema succcessfully', () => {
@@ -6,8 +6,6 @@
6
6
  "baseUrl": "./",
7
7
  "isolatedModules": true,
8
8
  "esModuleInterop": true,
9
- "paths": {
10
- "@/*": ["../src/*"]
11
- }
9
+ "paths": {}
12
10
  }
13
11
  }
@@ -0,0 +1,8 @@
1
+ import { program, Command } from '../src/utils/commander';
2
+
3
+ describe('utils', () => {
4
+ it('default', () => {
5
+ expect(program).toBeDefined();
6
+ expect(Command).toBeDefined();
7
+ });
8
+ });
package/tsconfig.json CHANGED
@@ -6,9 +6,7 @@
6
6
  "baseUrl": "./",
7
7
  "isolatedModules": true,
8
8
  "esModuleInterop": true,
9
- "paths": {
10
- "@/*": ["./src/*"]
11
- }
9
+ "paths": {}
12
10
  },
13
11
  "include": ["src"]
14
12
  }
@@ -1,101 +0,0 @@
1
- import { OutputConfig, ServerConfig, SourceConfig } from '.';
2
-
3
- const sourceDefaults: SourceConfig = {
4
- entries: undefined,
5
- disableDefaultEntries: false,
6
- entriesDir: './src',
7
- configDir: './config',
8
- apiDir: './api',
9
- envVars: [],
10
- globalVars: undefined,
11
- alias: undefined,
12
- moduleScopes: undefined,
13
- include: [],
14
- };
15
-
16
- const outputDefaults: OutputConfig = {
17
- assetPrefix: '/',
18
- htmlPath: 'html',
19
- jsPath: 'static/js',
20
- cssPath: 'static/css',
21
- mediaPath: 'static/media',
22
- path: 'dist',
23
- title: '',
24
- titleByEntries: undefined,
25
- meta: {
26
- charset: { charset: 'utf-8' },
27
- viewport:
28
- 'width=device-width, initial-scale=1.0, shrink-to-fit=no, viewport-fit=cover, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no',
29
- 'http-equiv': { 'http-equiv': 'x-ua-compatible', content: 'ie=edge' },
30
- renderer: 'webkit',
31
- layoutmode: 'standard',
32
- imagemode: 'force',
33
- 'wap-font-scale': 'no',
34
- 'format-detection': 'telephone=no',
35
- },
36
- metaByEntries: undefined,
37
- inject: 'head',
38
- injectByEntries: undefined,
39
- mountId: 'root',
40
- favicon: '',
41
- faviconByEntries: undefined,
42
- copy: undefined,
43
- scriptExt: undefined,
44
- disableHtmlFolder: false,
45
- disableCssModuleExtension: false,
46
- disableCssExtract: false,
47
- enableCssModuleTSDeclaration: false,
48
- disableMinimize: false,
49
- enableInlineStyles: false,
50
- enableInlineScripts: false,
51
- disableSourceMap: false,
52
- disableInlineRuntimeChunk: false,
53
- disableAssetsCache: false,
54
- enableLatestDecorators: false,
55
- polyfill: 'entry',
56
- dataUriLimit: 10000,
57
- templateParameters: {},
58
- templateParametersByEntries: undefined,
59
- cssModuleLocalIdentName: '[name]__[local]--[hash:base64:5]',
60
- enableModernMode: false,
61
- federation: undefined,
62
- disableNodePolyfill: false,
63
- enableTsLoader: false,
64
- };
65
-
66
- const serverDefaults: ServerConfig = {
67
- routes: undefined,
68
- publicRoutes: undefined,
69
- ssr: undefined,
70
- ssrByEntries: undefined,
71
- baseUrl: '/',
72
- port: 8080,
73
- };
74
-
75
- const devDefaults = { assetPrefix: false, https: false };
76
-
77
- const deployDefaults = {
78
- domain: '',
79
- domainByEntries: undefined,
80
- };
81
-
82
- const toolsDefaults = {
83
- webpack: undefined,
84
- babel: undefined,
85
- postcss: undefined,
86
- autoprefixer: undefined,
87
- lodash: undefined,
88
- devServer: undefined,
89
- tsLoader: undefined,
90
- terser: undefined,
91
- minifyCss: undefined,
92
- };
93
-
94
- export const defaults = {
95
- source: sourceDefaults,
96
- output: outputDefaults,
97
- server: serverDefaults,
98
- dev: devDefaults,
99
- deploy: deployDefaults,
100
- tools: toolsDefaults,
101
- };