Skip to content

Commit fd856d7

Browse files
authored
feat(StateManager): create the first version (DevExpress#30472)
1 parent 6e1c6c0 commit fd856d7

File tree

79 files changed

+2283
-217
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+2283
-217
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# See http://help.github.com/ignore-files/ for more about ignoring files.
22

3+
# tests
4+
__test-artifacts__
5+
36
# compiled output
47
npm
58
dist
@@ -26,7 +29,6 @@ node_modules
2629
!.vscode/launch.json
2730
!.vscode/extensions.json
2831

29-
3032
# System Files
3133
.DS_Store
3234
Thumbs.db

apps/react-storybook/.storybook/main.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { StorybookConfig } from "@storybook/react-webpack5";
22
import path from 'path';
3+
34
const getAbsolutePath = (packageName: string): any =>
45
path.dirname(require.resolve(path.join(packageName, 'package.json')));
56

apps/react-storybook/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
"scripts": {
77
"test": "echo \"Error: no test specified\" && exit 1",
88
"start": "storybook dev -p 6006",
9-
"build": "storybook build"
9+
"build": "storybook build",
10+
"serve": "http-server storybook-static -p 6006 -c-1"
1011
},
1112
"author": "",
1213
"license": "ISC",
@@ -26,6 +27,7 @@
2627
"@storybook/react": "7.6.19",
2728
"@storybook/react-webpack5": "7.6.19",
2829
"@storybook/test": "7.6.19",
30+
"http-server": "14.1.1",
2931
"prop-types": "15.8.1",
3032
"react": "18.0.0",
3133
"react-dom": "18.0.0",

packages/devextreme/build/gulp/context.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,6 @@ module.exports = {
1919
TRANSPILED_RENOVATION_PATH: 'artifacts/transpiled-renovation',
2020
TRANSPILED_PROD_ESM_PATH: 'artifacts/transpiled-esm-npm',
2121
SCSS_PACKAGE_PATH: '../devextreme-scss',
22-
EULA_URL: 'https://js.devexpress.com/Licensing/'
22+
EULA_URL: 'https://js.devexpress.com/Licensing/',
23+
REMOVE_NON_PRODUCTION_MODULE: argv.uglify,
2324
};

packages/devextreme/build/gulp/js-bundles.js

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ const ctx = require('./context.js');
1515
const headerPipes = require('./header-pipes.js');
1616
const webpackConfig = require('../../webpack.config.js');
1717
const env = require('./env-variables.js');
18-
1918
const namedDebug = lazyPipe()
2019
.pipe(named, (file) => path.basename(file.path, path.extname(file.path)) + '.debug');
2120

@@ -30,15 +29,18 @@ const DEBUG_BUNDLES = BUNDLES.concat([ '/bundles/dx.custom.js' ]);
3029

3130
const processBundles = (bundles, pathPrefix) => bundles.map((bundle) => pathPrefix + bundle);
3231
const muteWebPack = () => undefined;
33-
const getWebpackConfig = () => env.BUILD_INTERNAL_PACKAGE || env.BUILD_TEST_INTERNAL_PACKAGE ?
34-
Object.assign({
35-
plugins: [
36-
new webpack.NormalModuleReplacementPlugin(/(.*)\/license_validation/, resource => {
37-
resource.request = resource.request.replace('license_validation', 'license_validation_internal');
38-
})
39-
]
40-
}, webpackConfig) :
41-
webpackConfig;
32+
const getWebpackConfig = () => {
33+
const plugins = [];
34+
const isInternalBuild = env.BUILD_INTERNAL_PACKAGE || env.BUILD_TEST_INTERNAL_PACKAGE;
35+
36+
if (isInternalBuild) {
37+
plugins.push(new webpack.NormalModuleReplacementPlugin(/(.*)\/license_validation/, resource => {
38+
resource.request = resource.request.replace('license_validation', 'license_validation_internal');
39+
}));
40+
}
41+
42+
return Object.assign(webpackConfig, { plugins });
43+
};
4244

4345
const bundleProdPipe = lazyPipe()
4446
.pipe(named)
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
const fs = require('fs');
2+
const path = require('path');
3+
const through2 = require('through2');
4+
const Vinyl = require('vinyl');
5+
const replaceStateManagerModulesForProduction = require('../replace_state_manager_modules_for_production');
6+
const { removeDevelopmentStateManagerModules } = require('../remove_development_state_manager_modules');
7+
8+
const createEnvContent = (env) => ({
9+
index: [
10+
`export { setupStateManager } from './setup_state_manager';`,
11+
`export { signal } from './reactive_primitives/index';`
12+
].join('\n'),
13+
setupStateManager: `export const setupStateManager = () => {
14+
// this setupStateManager function body is for ${env} build
15+
}`,
16+
reactivePrimitivesIndex: `export const signal = () => {
17+
// this signal function body is for ${env} build
18+
}`,
19+
});
20+
21+
const PROD_DIR_CONTENT = createEnvContent('prod');
22+
const DEV_DIR_CONTENT = createEnvContent('dev');
23+
24+
const INDEX_DEV_CONTENT = `export { setupStateManager, signal } from './dev/index';`;
25+
const INDEX_PROD_CONTENT = `export * from './prod/index';`;
26+
27+
const FILE_OUTSIDE_OF_ENV_SPECIFIC_FOLDER_CONTENT = 'test content';
28+
const FILE_OUTSIDE_STATE_MANGER_CONTENT = 'console.log("file outside of state manager");';
29+
30+
describe('Build the state manager', () => {
31+
let testsContext;
32+
let originalConsoleError;
33+
let consoleErrorSpy;
34+
35+
const createEnvPaths = (baseDir, env) => ({
36+
reactivePrimitivesDir: path.join(baseDir, env, 'reactive_primitives'),
37+
reactivePrimitivesIndex: path.join(baseDir, env, 'reactive_primitives', 'index.js'),
38+
setupStateManager: path.join(baseDir, env, 'setup_state_manager.js'),
39+
index: path.join(baseDir, env, 'index.js'),
40+
});
41+
42+
const createEnvFiles = (paths, content) => {
43+
Object.entries(paths).forEach(([key, filePath]) => {
44+
if (filePath.endsWith('.js') && content[key]) {
45+
fs.writeFileSync(filePath, content[key]);
46+
}
47+
});
48+
};
49+
50+
const createEnvSpecificStreamFileObjects = (paths, content) => {
51+
return Object.entries(paths)
52+
.filter(([key, filePath]) => filePath.endsWith('.js') && content[key])
53+
.map(([key, filePath]) => new Vinyl({
54+
path: filePath,
55+
contents: Buffer.from(content[key])
56+
}));
57+
};
58+
59+
beforeEach(() => {
60+
const stream = replaceStateManagerModulesForProduction();
61+
const tempDir = path.join(__dirname, '__test-artifacts__');
62+
63+
if (fs.existsSync(tempDir)) {
64+
fs.rmSync(tempDir, { recursive: true, force: true });
65+
}
66+
67+
const devextremeDir = path.join(tempDir, 'devextreme');
68+
const stateManagerDir = path.join(devextremeDir, 'esm', '__internal', 'core', 'state_manager');
69+
const devDir = path.join(stateManagerDir, 'dev');
70+
const prodDir = path.join(stateManagerDir, 'prod');
71+
72+
const devPaths = createEnvPaths(stateManagerDir, 'dev');
73+
const prodPaths = createEnvPaths(stateManagerDir, 'prod');
74+
75+
const indexFilePath = path.join(stateManagerDir, 'index.js');
76+
const fileOutsideOfEnvSpecificFolderFilePath = path.join(stateManagerDir, 'state_manager.test.js');
77+
const fileOutsideStateMangerPath = path.join(tempDir, 'other_file.js');
78+
79+
fs.mkdirSync(stateManagerDir, { recursive: true });
80+
fs.mkdirSync(devDir, { recursive: true });
81+
fs.mkdirSync(prodDir, { recursive: true });
82+
fs.mkdirSync(devPaths.reactivePrimitivesDir, { recursive: true });
83+
fs.mkdirSync(prodPaths.reactivePrimitivesDir, { recursive: true });
84+
85+
fs.writeFileSync(indexFilePath, INDEX_DEV_CONTENT);
86+
fs.writeFileSync(fileOutsideOfEnvSpecificFolderFilePath, FILE_OUTSIDE_OF_ENV_SPECIFIC_FOLDER_CONTENT);
87+
fs.writeFileSync(fileOutsideStateMangerPath, FILE_OUTSIDE_STATE_MANGER_CONTENT);
88+
89+
createEnvFiles(devPaths, DEV_DIR_CONTENT);
90+
createEnvFiles(prodPaths, PROD_DIR_CONTENT);
91+
92+
originalConsoleError = console.error;
93+
consoleErrorSpy = jest.fn();
94+
console.error = consoleErrorSpy;
95+
96+
const files = [
97+
...createEnvSpecificStreamFileObjects(prodPaths, PROD_DIR_CONTENT),
98+
...createEnvSpecificStreamFileObjects(devPaths, DEV_DIR_CONTENT),
99+
new Vinyl({
100+
path: fileOutsideStateMangerPath,
101+
contents: Buffer.from(FILE_OUTSIDE_STATE_MANGER_CONTENT)
102+
}),
103+
new Vinyl({
104+
path: fileOutsideOfEnvSpecificFolderFilePath,
105+
contents: Buffer.from(FILE_OUTSIDE_OF_ENV_SPECIFIC_FOLDER_CONTENT)
106+
}),
107+
new Vinyl({
108+
path: indexFilePath,
109+
contents: Buffer.from(INDEX_DEV_CONTENT)
110+
}),
111+
];
112+
113+
stream.on('data', (file) => {
114+
fs.writeFileSync(file.path, file.contents.toString());
115+
});
116+
117+
files.forEach(file => stream.write(file));
118+
stream.end();
119+
120+
testsContext = {
121+
stream,
122+
devextremeDir,
123+
devDir,
124+
prodPaths,
125+
indexFilePath,
126+
fileOutsideOfEnvSpecificFolderFilePath,
127+
fileOutsideStateMangerPath
128+
};
129+
});
130+
131+
afterEach(() => {
132+
console.error = originalConsoleError;
133+
});
134+
135+
const runTestWithStream = (testFn) => {
136+
return (done) => {
137+
testsContext.stream.on('end', () => {
138+
try {
139+
testFn();
140+
done();
141+
} catch (error) {
142+
done(error);
143+
}
144+
});
145+
testsContext.stream.on('error', done);
146+
};
147+
};
148+
149+
it('should remove development modules', runTestWithStream(() => {
150+
removeDevelopmentStateManagerModules(testsContext.devextremeDir);
151+
152+
expect(fs.existsSync(testsContext.devDir)).toBe(false);
153+
expect(fs.existsSync(testsContext.fileOutsideOfEnvSpecificFolderFilePath)).toBe(false);
154+
expect(consoleErrorSpy).not.toHaveBeenCalled();
155+
}));
156+
157+
it('should not remove modules that are unrelated to the state manager', runTestWithStream(() => {
158+
removeDevelopmentStateManagerModules(testsContext.devextremeDir);
159+
160+
const fileOutsideStateMangerPathContent = fs.readFileSync(testsContext.fileOutsideStateMangerPath, 'utf8');
161+
expect(fileOutsideStateMangerPathContent).toBe(FILE_OUTSIDE_STATE_MANGER_CONTENT);
162+
}));
163+
164+
it('should replace `index.js` content by `prod/index.js` content', runTestWithStream(() => {
165+
removeDevelopmentStateManagerModules(testsContext.devextremeDir);
166+
167+
const indexFileContent = fs.readFileSync(testsContext.indexFilePath, 'utf8');
168+
expect(indexFileContent).toBe(INDEX_PROD_CONTENT);
169+
expect(fs.existsSync(testsContext.prodPaths.index)).toBe(true);
170+
expect(consoleErrorSpy).not.toHaveBeenCalled();
171+
}));
172+
});
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const path = require('path');
2+
3+
const STATE_MANAGER_FOLDER_PATH = path.join('__internal', 'core', 'state_manager');
4+
const STATE_MANAGER_INDEX_MODULE_PATH = path.join(STATE_MANAGER_FOLDER_PATH, 'index.js');
5+
const STATE_MANAGER_PROD_FOLDER_PATH = path.join(STATE_MANAGER_FOLDER_PATH, 'prod');
6+
7+
module.exports = {
8+
STATE_MANAGER_FOLDER_PATH,
9+
STATE_MANAGER_INDEX_MODULE_PATH,
10+
STATE_MANAGER_PROD_FOLDER_PATH
11+
};
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
require('./remove_development_state_manager_modules');
2+
require('./replace_state_manager_modules_for_production')
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
'use strict';
2+
3+
const path = require('path');
4+
const gulp = require('gulp');
5+
const del = require('del');
6+
const {
7+
STATE_MANAGER_FOLDER_PATH,
8+
STATE_MANAGER_INDEX_MODULE_PATH,
9+
STATE_MANAGER_PROD_FOLDER_PATH
10+
} = require('./constants');
11+
const ctx = require('../context');
12+
13+
const MODULE_TYPES = ['esm', 'cjs'];
14+
15+
const removeDevelopmentStateManagerModules = (targetPath) => {
16+
const patterns = [];
17+
18+
MODULE_TYPES.forEach(type => {
19+
patterns.push(`${path.join(targetPath, type, STATE_MANAGER_FOLDER_PATH)}/**`);
20+
});
21+
22+
MODULE_TYPES.forEach(type => {
23+
patterns.push(`!${path.join(targetPath, type, STATE_MANAGER_FOLDER_PATH)}`);
24+
patterns.push(`!${path.join(targetPath, type, STATE_MANAGER_INDEX_MODULE_PATH)}`);
25+
patterns.push(`!${path.join(targetPath, type, STATE_MANAGER_PROD_FOLDER_PATH)}`);
26+
patterns.push(`!${path.join(targetPath, type, STATE_MANAGER_PROD_FOLDER_PATH)}/**`);
27+
});
28+
29+
del.sync(patterns);
30+
}
31+
32+
const createRemoveDevelopmentStateManagerModulesTask = (targetPath) => (done) => {
33+
removeDevelopmentStateManagerModules(targetPath);
34+
done();
35+
};
36+
37+
gulp.task('state-manager-remove-development-only-modules-transpiled-prod-esm', createRemoveDevelopmentStateManagerModulesTask(ctx.TRANSPILED_PROD_ESM_PATH));
38+
39+
gulp.task('state-manager-remove-development-only-modules-transpiled-prod-renovation', createRemoveDevelopmentStateManagerModulesTask(ctx.TRANSPILED_PROD_RENOVATION_PATH));
40+
41+
module.exports = {
42+
removeDevelopmentStateManagerModules,
43+
createRemoveDevelopmentStateManagerModulesTask
44+
};
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
'use strict';
2+
3+
const gulp = require('gulp');
4+
const through2 = require('through2');
5+
const path = require('path');
6+
const fs = require('fs');
7+
const babel = require('@babel/core');
8+
const transpileConfig = require('../transpile-config');
9+
const {
10+
STATE_MANAGER_FOLDER_PATH,
11+
STATE_MANAGER_INDEX_MODULE_PATH,
12+
} = require('./constants');
13+
const ctx = require('../context');
14+
15+
const ERROR_PREFIX = 'Error during replacing the state manager modules:';
16+
17+
function replaceStateManagerModulesForProduction() {
18+
return through2.obj(function(file, enc, callback) {
19+
if (file.path.includes(STATE_MANAGER_INDEX_MODULE_PATH)) {
20+
try {
21+
file.contents = Buffer.from(`export * from './prod/index';`);
22+
} catch (error) {
23+
console.error(ERROR_PREFIX, error);
24+
}
25+
}
26+
27+
callback(null, file);
28+
});
29+
}
30+
31+
const prepareStateManager = (dist) => gulp.series.apply(gulp, [
32+
() => gulp
33+
.src(`${dist}/**/${STATE_MANAGER_FOLDER_PATH}/**`)
34+
.pipe(replaceStateManagerModulesForProduction())
35+
.pipe(gulp.dest(dist)),
36+
]);
37+
38+
gulp.task('state-manager-replace-production-modules-transpiled-prod-esm', prepareStateManager(ctx.TRANSPILED_PROD_ESM_PATH));
39+
40+
gulp.task('state-manager-replace-production-modules-transpiled-prod-renovation', prepareStateManager(ctx.TRANSPILED_PROD_RENOVATION_PATH));
41+
42+
module.exports = replaceStateManagerModulesForProduction;

0 commit comments

Comments
 (0)