Skip to content

Commit

Permalink
done #129 & update tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Diablohu committed Jul 16, 2019
1 parent 0c5e082 commit 326f7fb
Show file tree
Hide file tree
Showing 12 changed files with 503 additions and 340 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,3 @@ package-lock.json
test/cases/config/samples
logs
dist
/playground.js
9 changes: 6 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@
核心

- **新特性**
- **新全局函数** `getCache()` - 获取公用缓存空间。具体用法请参见文档 [全局与工具函数/全局函数](https://koot.js.org/#/utilities?id=全局函数)
- **新全局函数** `getCache()` - 获取公用缓存空间。具体用法请参见文档 [全局与工具函数/全局函数](https://koot.js.org/#/utilities?id=全局函数) ([#143](https://github.com/cmux/koot/issues/143))
- 现在会自动为客户端打包结果中的部分资源文件生成 gzip 版本 (.gz 文件) ([#129](https://github.com/cmux/koot/issues/129))
- **优化**
- `createStore()` 全局函数现允许传入 store 增强函数 (enhancer)。详情请参见文档 [Store/全局函数 createStore](https://koot.js.org/#/store?id=全局函数-createstore)
- `createStore()` 全局函数现允许传入 store 增强函数 (enhancer)。详情请参见文档 [Store/全局函数 createStore](https://koot.js.org/#/store?id=全局函数-createstore) ([#144](https://github.com/cmux/koot/issues/144))
- 多语言翻译函数 (`__()`) 现支持返回一个对象或数组
- SSR
- _服务器端_: 现支持有超大型语言包的项目
- _服务器端_: 现支持有超大型语言包的项目 ([#145](https://github.com/cmux/koot/issues/145))
- 开发环境
- _客户端_:减少部分初始的日志输出
- 添加依赖包
- `compression-webpack-plugin`
- 更新依赖包
- minor
- `inquirer` -> _6.5.0_
Expand Down
10 changes: 8 additions & 2 deletions packages/koot-webpack/factory-config/transform-config-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ const fs = require('fs-extra');
const path = require('path');
const webpack = require('webpack');
const DefaultWebpackConfig = require('webpack-config').default;
const CopyWebpackPlugin = require('copy-webpack-plugin');

const CopyWebpackPlugin = require('copy-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const KootI18nPlugin = require('../plugins/i18n');
const DevModePlugin = require('../plugins/dev-mode');
const SpaTemplatePlugin = require('../plugins/spa-template');
Expand Down Expand Up @@ -200,8 +201,8 @@ module.exports = async (kootConfigForThisBuild = {}) => {
result.output.pathinfo = false;
}

// 处理 entry
{
// 处理 entry
if (typeof result.entry === 'object' && !result.entry.client) {
result.entry.client = defaultClientEntry;
} else if (typeof result.entry !== 'object') {
Expand Down Expand Up @@ -344,6 +345,11 @@ module.exports = async (kootConfigForThisBuild = {}) => {
);
}
}

// 生产环境专用
if (ENV === 'prod') {
result.plugins.push(new CompressionPlugin());
}
}

return await transformConfigLast(result, kootConfigForThisBuild);
Expand Down
1 change: 1 addition & 0 deletions packages/koot/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
"classlist-polyfill": "1.2.0",
"cli-spinners": "2.2.0",
"commander": "2.20.0",
"compression-webpack-plugin": "3.0.0",
"cookie": "0.4.0",
"copy-webpack-plugin": "5.0.3",
"copyfiles": "2.1.1",
Expand Down
148 changes: 148 additions & 0 deletions playground/babel-transform-routes-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
const fs = require('fs-extra');
const path = require('path');
const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const t = require('@babel/types');
const generate = require('@babel/generator').default;

const entry = path.resolve(
__dirname,
'../test/projects/standard/src/router/index.js'
);

const transformFile = async (file = entry) => {
const source = await fs.readFile(file, 'utf-8');
const ast = parser.parse(source, {
sourceType: 'unambiguous',
plugins: ['dynamicImport']
});

console.log(ast.program);

traverse(ast, {
// TODO: 移除 `routeCheck` 引用
// TODO: 转换属性 `component`
// TODO: 转换属性 `getComponent`
// TODO: 转换属性 `indexRoute`
// TODO: 转换属性 `childRoutes`
ObjectExpression: (path, state) => {
if (t.isObjectExpression(path.node.left, { name: 'indexRoute' })) {
console.log('\n\n\n\n');
console.log(path, state);
// console.log(path.key);
}
}
});

const result = generate(ast, {}, source);
// console.log(result);
await fs.writeFile(
path.resolve(__dirname, '../logs/routes.js'),
result.code,
'utf-8'
);
};

(async () => {
await transformFile();
})();

// (async () => {
// const source = await fs.readFile(file, 'utf-8');
// const ast = parser.parse(source, {
// sourceType: 'unambiguous',
// plugins: ['@babel/plugin-syntax-dynamic-import', 'dynamicImport']
// });

// let r = '';
// /** @type {Number} 上次截取的代码的结尾位置,作为下次处理的起始位置 */
// let rLastPosition = 0;
// /** @type {String} `routeCheck` 的函数名 */
// let nameRouteCheck;

// const append = (start, end) => {
// r += source.substr(rLastPosition, start - rLastPosition);
// rLastPosition = end;
// };

// const transformRoutesNode = node => {
// const value = node.declaration || node.value;
// const { start, end, properties } = value;

// let src = source.substr(start, end - start);

// if (Array.isArray(properties))
// properties.forEach(property => {
// console.log('\n\n\n\n');

// const key = property.key ? property.key.name : '';

// switch (key) {
// // TODO: 转换属性 `component`
// case 'component': {
// break;
// }
// // TODO: 转换属性 `getComponent`
// case 'getComponent': {
// break;
// }
// // TODO: 转换属性 `indexRoute`
// case 'indexRoute': {
// transformRoutesNode(property);
// break;
// }
// // TODO: 转换属性 `childRoutes`
// case 'childRoutes': {
// break;
// }
// default: {
// }
// }
// });

// return src;
// };

// const {
// program: { body: nodes }
// } = ast;

// /** @type {Node} `export default` */
// const nodeExportDefault = nodes
// .filter(node => node.type === 'ExportDefaultDeclaration')
// .reduce((_, node) => node, '');

// // ========================================================================
// // 移除 `routeCheck` 引用
// // ========================================================================
// const nodeImportRouteCheck = nodes
// .filter(
// node =>
// node.type === 'ImportDeclaration' &&
// /route-check($|\.js)/.test(node.source.value)
// )
// .reduce((_, node) => node, '');
// if (
// typeof nodeImportRouteCheck === 'object' &&
// nodeImportRouteCheck.specifiers
// ) {
// const { specifiers } = nodeImportRouteCheck;
// nameRouteCheck = specifiers[0].local.name;
// append(nodeImportRouteCheck.start, nodeImportRouteCheck.end);
// }

// // ========================================================================
// // 如果 ExportDefaultDeclaration 为直接输出对象
// // ========================================================================
// if (
// nodeExportDefault.declaration &&
// nodeExportDefault.declaration.type === 'ObjectExpression'
// ) {
// const { start, end } = nodeExportDefault.declaration;
// append(start, end);
// r += transformRoutesNode(nodeExportDefault);
// }

// // ========================================================================
// await fs.writeFile(path.resolve(__dirname, 'logs/routes.js'), r, 'utf-8');
// })();
56 changes: 56 additions & 0 deletions playground/puppeteer-test-all-response-gzip.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
const fs = require('fs-extra');
const path = require('path');
const puppeteer = require('puppeteer');

const origin = 'http://localhost:8080/';
const dist = path.resolve(__dirname, '../test/projects/standard/dist-spa-test');

const getUriFromChunkmap = async file => {
const chunkmap = await fs.readJson(
path.resolve(dist, '.public-chunkmap.json')
);
const getMap = (map = chunkmap) => {
if (
typeof map['.entrypoints'] === 'object' &&
typeof map['.files'] === 'object'
)
return map;

const keys = Object.keys(map);

if (!keys.length) return {};

return getMap(map[keys[0]]);
};
const map = getMap();
const p = map['.public'] || '';
const files = map['.files'] || {};

if (files[file]) return files[file].replace(new RegExp(`^${p}`), '');

return '';
};

(async () => {
const uri = await getUriFromChunkmap('client.js');
console.log({ uri });

const browser = await puppeteer.launch();
const context = await browser.createIncognitoBrowserContext();
const page = await context.newPage();

const res = await page.goto(origin + uri, {
waitUntil: 'networkidle0'
});
const headers = res.headers();
// const json = await res.json();

console.log({
'content-encoding': headers['content-encoding'],
'content-length': headers['content-length'],
length: (await res.text()).length
// json
});

await browser.close();
})();
43 changes: 42 additions & 1 deletion test/cases/puppeteer-test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const puppeteer = require('puppeteer');
const getUriFromChunkmap = require('../libs/get-uri-from-chunkmap');

/**
* puppeteer 测试
Expand Down Expand Up @@ -150,9 +151,49 @@ const requestHidden404 = async (origin, browser) => {
expect(res.status()).toBe(404);
};

/**
* puppeteer 测试
*
* 请求关键资源文件应为 gzip
* @async
* @param {string} origin
* @param {string} dist
* @param {Object} [browser]
* @returns {Promise}
*/
const criticalAssetsShouldBeGzip = async (origin, dist, browser) => {
let needToClose = !browser;

if (!browser)
browser = await puppeteer.launch({
headless: true
});

const uri = await getUriFromChunkmap(dist, 'client.js');
expect(!!uri).toBe(true);

const context = await browser.createIncognitoBrowserContext();
const page = await context.newPage();
const res = await page.goto(
`${origin}${uri.substr(0, 1) === '/' ? '' : '/'}${uri}`,
{
waitUntil: 'domcontentloaded'
}
);
const headers = res.headers();
const text = await res.text();

await context.close();
if (needToClose) await browser.close();

expect(headers['content-encoding']).toBe('gzip');
expect(parseInt(headers['content-length']) <= text.length).toBe(true);
};

module.exports = {
styles,
customEnv,
injectScripts,
requestHidden404
requestHidden404,
criticalAssetsShouldBeGzip
};
16 changes: 10 additions & 6 deletions test/cases/react-base/need-in-order.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ const {
styles: puppeteerTestStyles,
customEnv: puppeteerTestCustomEnv,
injectScripts: puppeteerTestInjectScripts,
requestHidden404: testRequestHidden404
requestHidden404: testRequestHidden404,
criticalAssetsShouldBeGzip: testAssetsGzip
} = require('../puppeteer-test');
const addCommand = require('../../libs/add-command-to-package-json');
const terminate = require('../../libs/terminate-process');
Expand Down Expand Up @@ -89,9 +90,10 @@ afterEach(() => {});
* 测试项目
* @async
* @param {Number} port
* @param {string} dist
* @param {Object} settings
*/
const doTest = async (port, settings = {}) => {
const doTest = async (port, dist, settings = {}) => {
const { isDev = false, enableJavascript = true, customEnv = {} } = settings;
customEnv.notexist = undefined;

Expand Down Expand Up @@ -173,6 +175,7 @@ const doTest = async (port, settings = {}) => {
await puppeteerTestCustomEnv(page, customEnv);
await puppeteerTestInjectScripts(page);
await testRequestHidden404(origin, browser);
if (!isDev) await testAssetsGzip(origin, dist, browser);

// TODO: 在设置了 sw 时有 sw 注册且没有报错

Expand Down Expand Up @@ -277,10 +280,10 @@ describe('测试: React 同构项目', () => {
);

await testFilesFromChunkmap(dist);
await doTest(port, {
await doTest(port, dist, {
customEnv
});
await doTest(port, {
await doTest(port, dist, {
enableJavascript: false,
customEnv
});
Expand All @@ -296,6 +299,7 @@ describe('测试: React 同构项目', () => {
await beforeTest(dir);

// const port = '8316'
const dist = path.resolve(dir, 'dist');
const customEnv = {
aaaaa: '' + Math.floor(Math.random() * 10000),
bbbbb: 'a1b2c3'
Expand Down Expand Up @@ -324,11 +328,11 @@ describe('测试: React 同构项目', () => {
// })
expect(errors.length).toBe(0);

await doTest(port, {
await doTest(port, dist, {
isDev: true,
customEnv
});
await doTest(port, {
await doTest(port, dist, {
isDev: true,
customEnv,
enableJavascript: false
Expand Down
Loading

0 comments on commit 326f7fb

Please sign in to comment.