提交 b9d4e395 编写于 作者: Y yylgit

delete

上级 93333b1f
......@@ -30,7 +30,10 @@ module.exports = function(options) {
]
},
plugins: [
new MvvmGraphPlugin()
new MvvmGraphPlugin({
cmlType: type,
media
})
]
};
// options.moduleIdType = 'hash';
......
const CMLNode = require('./cmlNode.js');
const path = require('path');
const MvvmCompiler = require('./compiler.js');
class mvvmGraphPlugin {
constructor() {
this.moduleRule = [ // 文件后缀对应module信息
{
test: /\.css|\.less$/,
moduleType: 'style',
attrs: {
lang: 'less'
}
},
{
test: /\.stylus|\.styls$/,
moduleType: 'style',
attrs: {
lang: 'stylus'
}
},
{
test: /\.js|\.interface$/,
moduleType: 'script'
},
{
test: /\.json$/,
moduleType: 'json'
},
{
test: /\.(png|jpe?g|gif|svg|mp4|webm|ogg|mp3|wav|flac|aac|woff|woff2?|eot|ttf|otf)(\?.*)?$/,
moduleType: 'asset'
}
]
constructor(options = {}) {
this.options = options;
}
apply(compiler) {
let npmName = cml.config.get().extPlatform[this.options.cmlType];
let PlatformPlugin = require(path.join(cml.projectRoot, 'node_modules', npmName)); // eslint-disable-line
let plugin = new PlatformPlugin(this.options);
let mvvmCompiler = new MvvmCompiler(compiler);
plugin.register(mvvmCompiler);
compiler.plugin('should-emit', function(compilation) {
mvvmCompiler.run(compilation.modules);
......
此差异已折叠。
# Editor directories and files
.DS_Store
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
.vscode
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
jspm_packages/
# Typescript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# ///////////////////////////
#### mvvm协议标准中处理cml文件style部分与样式文件
\ No newline at end of file
const lessCompile = require('./lib/lessCompile');
const mediaCompile = require('./lib/mediaCompile');
const standardCompile = require('./lib/standardCompile');
const stylusCompile = require('./lib/stylusCompile');
exports.lessCompile = lessCompile;
exports.mediaCompile = mediaCompile;
exports.standardCompile = standardCompile;
exports.stylusCompile = stylusCompile;
const postcss = require('postcss');
module.exports = function({source, filePath, compiler}) {
let deps = [];
const assetsPlugin = postcss.plugin('postcss-assets-plugin', function(options) {
return (root, result) => {
root.walkDecls((decl, i) => {
if (~decl.value.indexOf('url')) {
decl.value = decl.value.replace(/url\s*\(\s*[\'\"]?(.+?)[\'\"]?\s*\)/g, function(all, $1) {
let realDependPath = compiler.resolve(filePath, $1);
deps.push(realDependPath);
let publicPath = compiler.getPublicPath(realDependPath);
return publicPath;
})
}
})
}
})
return {
source: postcss([assetsPlugin]).process(source).css,
deps
}
}
const less = require('less');
module.exports = function({source, filePath}) {
// todo 给less options 添加plugin addFileManager 参见less-loader createWebpackLessPlugin.js
return less.render(source, {
compress: false,
filename: filePath,
relativeUrls: true,
sourceMap: false
})
}
module.exports = function(source = '', cmlType) {
let reg = /@media\s*cml-type\s*\(([\w\s,]*)\)\s*/g;
if (!reg.test(source)) {
return source;
}
reg.lastIndex = 0;
while (true) { // eslint-disable-line
let result = reg.exec(source);
if (!result) {break;}
let cmlTypes = result[1] || '';
cmlTypes = cmlTypes.split(',').map(item => item.trim());
let isSave = ~cmlTypes.indexOf(cmlType);
let startIndex = result.index; // @media的开始
let currentIndex = source.indexOf('{', startIndex); // 从第一个@media开始
let signStartIndex = currentIndex; // 第一个{的位置
if (currentIndex == -1) {
throw new Error("@media cml-type format err");
}
let signStack = [];
signStack.push(0);
while (signStack.length > 0) {
let index1 = source.indexOf('{', currentIndex + 1);
let index2 = source.indexOf('}', currentIndex + 1);
let index;
// 都有的话 index为最前面的
if (index1 !== -1 && index2 !== -1) {
index = Math.min(index1, index2);
} else {
index = Math.max(index1, index2);
}
if (index === -1) {
throw new Error("@media cml-type format err");
}
let sign = source[index];
currentIndex = index;
if (sign === '{') {
signStack.push(signStack.length);
} else if (sign === '}') {
signStack.pop();
}
}
// 操作source
if (isSave) { // 保存的@media
var sourceArray = Array.from(source);
sourceArray.splice(startIndex, currentIndex - startIndex + 1, source.slice(signStartIndex + 1, currentIndex));
source = sourceArray.join('');
} else { // 删除的
source = source.slice(0, startIndex) + source.slice(currentIndex + 1);
}
reg.lastIndex = 0;
}
return source;
}
const mediaCompile = require('./mediaCompile.js');
const lessCompile = require('./lessCompile.js');
const stylusCompile = require('./stylusCompile.js');
const assetsCompile = require('./assetsCompile.js');
module.exports = async function({source, filePath, cmlType, lang = 'less', compiler}) {
let imports = [];
source = mediaCompile(source, cmlType);
if (lang === 'less') {
let lessResult = await lessCompile({source, filePath});
source = lessResult.css;
imports = lessResult.imports;
}
if (lang === 'stylus' || lang === 'styl') {
let stylusResult = await stylusCompile({source, filePath});
source = stylusResult.css;
imports = stylusResult.imports;
}
if (compiler) {
let {source: newSource, deps} = assetsCompile({source, filePath, compiler})
source = newSource;
imports = imports.concat(deps);
}
return {
output: source,
imports // 返回依赖文件的绝对路径
};
}
const stylus = require('stylus');
const path = require('path');
module.exports = function({source, filePath}) {
// todo 给less options 添加plugin addFileManager 参见less-loader createWebpackLessPlugin.js
return new Promise(function(resolve, reject) {
let styl = stylus(output);
styl.set('filename', filePath);
styl.set('paths', [path.dirname(filePath)]);
let imports = styl.deps();
stylus.render(function(err, css) {
if (err) {
reject(err);
}
resolve({
imports,
css
});
})
})
}
{
"name": "mvvm-style-parser",
"version": "0.3.0-alpha.2",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"chameleon-tool-utils": "0.3.0-alpha.2",
"less": "3.9.0",
"postcss": "^7.0.14",
"stylus": "0.54.5"
}
}
\ No newline at end of file
let assetsCompile = require('../lib/assetsCompile.js');
const source = `
.class1 {
background: url(./images/1.png?__inline) no-repeat;
}
@font-face{
font-family: 'YaHei Consolas Hybrid';
src : url('./fonts/test.ttf');
}
`
const mvvmpack = require('../../mvvm-pack/index.js');
const path = require('path');
let compiler = new mvvmpack({
config: {
check: {
enable: true, // 是否开启接口校验
enableTypes: [] // 接口校验支持的类型 可以开启["Object","Array","Nullable"]
},
output: {
publicPath: 'http://static.cml.com/static/'
}
},
logLevel: 3,
cmlType: 'web',
media: 'dev',
cmlRoot: path.resolve('./'),
projectRoot: path.resolve(__dirname, './')
})
let result = assetsCompile({source, filePath: path.join(__dirname, './assetsCompile.js'), compiler});
console.log(result)
@import './2.styl';
\ No newline at end of file
body
font: 12px Helvetica, Arial, sans-serif
a.button
-webkit-border-radius: 5px
-moz-border-radius: 5px
border-radius: 5px
\ No newline at end of file
var path = require('path');
var fs = require('fs');
var stylus = require('stylus')
let source = fs.readFileSync(path.join(__dirname, './1.styl'), {encoding: 'utf8'});
let filename = path.join(__dirname, './1.styl');
let demo = stylus(source)
demo.set('filename', filename)
demo.set('paths', [path.dirname(filename)]);
let deps = demo.deps();
debugger
console.log(deps)
demo.render(function(err, css) {
console.log(css)
})
let lessCompile = require('../../lib/lessCompile');
var path = require('path');
var fs = require('fs');
let filePath = path.join(__dirname, './less1.less')
let source = fs.readFileSync(filePath, {encoding: 'utf8'});
debugger
lessCompile({
source,
filePath
}).then(res => {
console.log(res)
})
@import './less2.less';
\ No newline at end of file
.name {
font-size: 2px;
}
\ No newline at end of file
let standard = require('../lib/standardCompile.js');
const source = `
.class1 {
background: url(./images/1.png) no-repeat;
}
@font-face{
font-family: 'YaHei Consolas Hybrid';
src : url('./fonts/test.ttf');
}
`
const mvvmpack = require('../../mvvm-pack/index.js');
const path = require('path');
let compiler = new mvvmpack({
config: {
check: {
enable: true, // 是否开启接口校验
enableTypes: [] // 接口校验支持的类型 可以开启["Object","Array","Nullable"]
},
output: {
publicPath: 'http://static.cml.com/static/'
}
},
logLevel: 3,
cmlType: 'web',
media: 'dev',
cmlRoot: path.resolve('./'),
projectRoot: path.resolve(__dirname, './')
})
let result = standard({ cmlType: 'web', lang: 'less', source, filePath: path.join(__dirname, './assetsCompile.js'), compiler});
console.log(result.output)
console.log(result.imports)
此差异已折叠。
module.exports = require('./lib/MVVMPack.js');
const fs = require('fs');
class CMLNode {
constructor(options = {}) {
this.ext;
this.realPath; // 文件物理地址
this.nodeType; // App/Page/Component/Module // 节点类型 CML文件分为App/Page/Component 其他的为Module CML文件中的每一个部分也是一个Node节点
this.moduleType; // template/style/script/json/asset CML为CML文件
this.dependencies = []; // 该节点的直接依赖编译及诶点 app.cml依赖pages.cml pages.cml依赖components.cml js依赖js cmss依赖cmss
this.devDependencies = []; // 该节点的编译依赖的文件 该文件改动会触发重新编译 但是这个文件本身是不需要单独编译
this.childrens = []; // 子模块 CML才有子模块
this.parent; // 父模块 CML文件中的子模块才有
this.source; // 模块源代码
this.convert; // AST JSON
this.output; // 模块输出 各种过程操作该字段
this.attrs; // template/style/script/json模块上的属性
this.compiled; // 是否经过编译
this.extra; // 用户可以额外添加的信息
this.mtime; // 文件修改时间
this.identifier; // 节点唯一标识 由 nodeType moduleType realPath组成
this.modId; // script 与asset模块的id 用于amd包装
Object.keys(options).forEach(key => {
this[key] = options[key];
})
}
// 文件的修改时间map todo
notChange(fileTimestamps) {
let depNodes = this.getDependenciesNode();
let result = depNodes.every(node => {
let result = fs.statSync(node.realPath.split('?')[0]).mtime.getTime() === node.mtime;
return result;
})
return result;
}
getDependenciesNode(depList = []) {
if (!~depList.indexOf(this)) {
depList.push(this);
let newDepList = [];
newDepList = newDepList.concat(this.dependencies, this.devDependencies, this.childrens);
newDepList = [...new Set(newDepList)];
newDepList.forEach(item => {
item.getDependenciesNode(depList);
})
}
return depList;
}
getDependenciesFilePaths() {
let depNodes = this.getDependenciesNode();
let result = depNodes.map(node => {
return node.realPath.split('?')[0];
})
return result;
}
}
module.exports = CMLNode;
const Event = require('./Event');
const ResolveFactory = require('./ResolveFactory');
const NodeEnvironmentPlugin = require('./FileSystem/NodeEnvironmentPlugin.js');
const CMLNode = require('./CMLNode.js');
const path = require('path');
const cmlUtils = require('chameleon-tool-utils');
const CMLParser = require('mvvm-template-parser');
const CMSSParser = require('mvvm-style-parser');
const JSParser = require('mvvm-script-parser');
const fs = require('fs');
const Watching = require('./Watching');
const Log = require('./Log');
const {AMDWrapper} = require('./Mod/index.js');
const mime = require('mime');
class Compile {
constructor(options) {
this.options = options; // cmlType, media, projectRoot, cmlRoot, config
this.graphNodeMap = {}; // 创建graph的时候就要注意 同一个文件只能创建一个node 所以要记录有了哪些node key 为filePath + moduleType value为Node对象
this.projectGraph; // 编译图
this.oneLoopCompiledNode = []; // 一次编译过程编译到的节点,利用这个队列去触发用户的编译,实现了用户编译的缓存。
this.event = new Event();
this._resolve = ResolveFactory(options);
this.fileDependencies = new Set(); // 整个编译过程依赖的文件 编译完成后watch需要
new NodeEnvironmentPlugin().apply(this); // 文件系统
this.stringExt = /\.(cml|interface|js|json|css|less|sass|scss|stylus|styl)(\?.*)?$/;// 转成utf-8编码的文件后缀
this.nodeType = {
App: 'App',
Page: 'Page',
Component: 'Component',
Module: 'Module'
}
this.moduleType = {
template: "template",
style: "style",
script: "script",
json: "json",
asset: "asset",
other: "other"
}
this.compileQueue = []; // 待编译节点列表
this.log = new Log({
level: options.logLevel || 2
})
this.assetsPath = 'static/assets/${filename}_${hash}.${ext}'; // 资源的发布路径 配合publicPath
this.moduleRule = [ // 文件后缀对应module信息
{
test: /\.css|\.less$/,
moduleType: this.moduleType.style,
attrs: {
lang: 'less'
}
},
{
test: /\.stylus|\.styls$/,
moduleType: this.moduleType.style,
attrs: {
lang: 'stylus'
}
},
{
test: /\.js|\.interface$/,
moduleType: this.moduleType.script
},
{
test: /\.json$/,
moduleType: this.moduleType.json
},
{
test: /\.(png|jpe?g|gif|svg|mp4|webm|ogg|mp3|wav|flac|aac|woff|woff2?|eot|ttf|otf)(\?.*)?$/,
moduleType: this.moduleType.asset
}
]
this.resolve = this.resolve.bind(this);
this.outputFileList = {}; // 输出对象
}
// path 是源文件路径 request是要解析的路径
resolve(start, request, context = {}) {
start = path.dirname(start);
return this._resolve.resolveSync(context, start, request)
}
watch(watchOptions, handler) {
this.fileTimestamps = {};
this.contextTimestamps = {};
const watching = new Watching(this, watchOptions, handler);
return watching;
}
async run() {
this.log.debug('start run!');
this.emit('start-run', Date.now());
// 每次编译要清空 编译图 重新计算 this.graphNodeMap做缓存
this.projectGraph = null;
this.fileDependencies = new Set();
this.oneLoopCompiledNode = [];
await this.createProjectGraph();
await this.customCompile();
this.emit('end-run', (Date.now()));
}
hook(eventName, func) {
this.event.on(eventName, func);
}
emit(eventName, ...params) {
this.log.debug('emit log:' + eventName + 'params:' + params)
this.event.emit(eventName, ...params);
}
// 触发事件 带通用参数
commonEmit(eventName, currentNode) {
let params = {
currentNode,
compile: this
}
this.event.emit(eventName, params);
}
// 创建依赖图
async createProjectGraph() {
this.createAppAndPage();
// 层级编译
while (this.compileQueue.length) {
let node = this.compileQueue.shift();
await this.compileNode(node);
}
this.log.debug('standard compile end!');
}
// 创建App和Page节点
createAppAndPage() {
let {projectRoot} = this.options;
let root = this.projectGraph = this.createNode({
realPath: path.join(this.options.projectRoot, 'src/app/app.cml'),
nodeType: this.nodeType.App
})
this.compileQueue.push(root);
let {hasError, routerConfig} = this.getRouterConfig();
if (!hasError) {
let routes = routerConfig.routes;
routes.forEach(route => {
let cmlFilePath = path.join(projectRoot, 'src', route.path + '.cml');
if (cmlUtils.isFile(cmlFilePath)) {
let pageNode = this.createNode({
realPath: cmlFilePath,
nodeType: this.nodeType.Page
})
root.dependencies.push(pageNode)
}
})
}
}
// 路由变化何时要触发编译
getRouterConfig() {
let {projectRoot} = this.options;
// 有配置路由文件,给app.json添加pages
let routerConfigPath = path.join(projectRoot, 'src/router.config.json');
let routerConfig = {};
let hasError = false;
try {
let content = this.readFileSync(routerConfigPath);
content = this.utf8BufferToString(content);
routerConfig = JSON.parse(content);
} catch (e) {
hasError = true;
}
return {
hasError,
routerConfig
};
}
async compileNode(currentNode) {
if (currentNode.compiled) {
return;
}
this.oneLoopCompiledNode.push(currentNode);
this.log.debug('standard compiled node: ' + currentNode.identifier)
switch (currentNode.nodeType) {
case this.nodeType.App:
await this.compileCMLFile(currentNode)
break;
case this.nodeType.Page:
await this.compileCMLFile(currentNode)
break;
case this.nodeType.Component:
await this.compileCMLFile(currentNode)
break;
case this.nodeType.Module:
await this.compileModule(currentNode);
break;
default:
throw new Error('not find nodeType '+ currentNode.nodeType);
}
// 实现层级编译
// 子模块的编译
if (currentNode.childrens && currentNode.childrens.length > 0) {
for (let i = 0; i < currentNode.childrens.length; i++) {
this.compileQueue.push(currentNode.childrens[i]);
}
}
// 依赖节点的编译
if (currentNode.dependencies && currentNode.dependencies.length > 0) {
for (let i = 0; i < currentNode.dependencies.length; i++) {
this.compileQueue.push(currentNode.dependencies[i]);
}
}
currentNode.compiled = true;
}
readNodeSource(currentNode) {
let buf = this.readFileSync(cmlUtils.delQueryPath(currentNode.realPath));
currentNode.ext = path.extname(currentNode.realPath);
if (this.stringExt.test(currentNode.ext)) {
buf = this.utf8BufferToString(buf);
}
currentNode.source = buf;
}
readFileSync(...args) {
return this.inputFileSystem.readFileSync.apply(this.inputFileSystem, args);
}
compileCMLFile(currentNode) {
let realPath = currentNode.realPath;
let content = currentNode.source;
let parts = cmlUtils.splitParts({content});
if (parts.template && parts.template[0]) {
let item = parts.template[0];
let newNode = this.createNode({
realPath,
nodeType: this.nodeType.Module,
moduleType: this.moduleType.Template,
source: item.content
})
newNode.attrs = item.attrs;
newNode.parent = currentNode;
currentNode.childrens.push(newNode);
}
if (parts.script && parts.script.length > 0) {
parts.script.forEach(item => {
let moduleType = item.cmlType === 'json' ? this.moduleType.json : this.moduleType.script;
let newNode = this.createNode({
realPath,
nodeType: this.nodeType.Module,
moduleType,
source: item.content
})
newNode.attrs = item.attrs;
newNode.parent = currentNode;
currentNode.childrens.push(newNode);
})
}
if (parts.style && parts.style[0]) {
let item = parts.style[0];
let newNode = this.createNode({
realPath,
nodeType: this.nodeType.Module,
moduleType: this.moduleType.style,
source: item.content
})
newNode.attrs = item.attrs;
newNode.parent = currentNode;
currentNode.childrens.push(newNode);
}
}
async compileModule(currentNode) {
switch (currentNode.moduleType) {
case this.moduleType.Template:
await this.compileTemplate(currentNode);
break;
case this.moduleType.style:
await this.compileStyle(currentNode);
break;
case this.moduleType.script:
await this.compileScript(currentNode);
break;
case this.moduleType.json:
await this.compileJson(currentNode);
break;
case this.moduleType.assets:
await this.compileAsset(currentNode);
break;
case this.moduleType.other:
// await this.compileOther(currentNode);
break;
default:
throw new Error('not find compile Module Type ' + currentNode.moduleType)
}
}
async compileTemplate(currentNode) {
let {convert, output} = await CMLParser.standardParser({
source: currentNode.source,
lang: currentNode.attrs.lang
});
currentNode.convert = convert;
currentNode.output = output;
}
async compileStyle(currentNode) {
let {output, imports} = await CMSSParser.standardCompile({
source: currentNode.source,
filePath: currentNode.realPath,
cmlType: this.options.cmlType,
lang: currentNode.attrs.lang,
compiler: this
});
currentNode.output = output;
imports.forEach(item => {
let newNode = this.createNode({
realPath: item,
nodeType: this.nodeType.Module,
moduleType: this.moduleType.style
})
currentNode.devDependencies.push(newNode);
})
}
async compileScript(currentNode) {
let self = this;
let { cmlType, media, config} = this.options;
let {source, devDeps} = await JSParser.standardParser({
cmlType,
media,
source: currentNode.source,
filePath: currentNode.realPath,
check: config.check,
resolve: this.resolve
});
for (let i = 0; i < devDeps.length; i++) {
let dependPath = devDeps[i];
let dependNode = self.createNode({
realPath: dependPath,
nodeType: self.nodeType.Module
})
currentNode.devDependencies.push(dependNode);
}
let result = await JSParser.JSCompile({source, filePath: currentNode.realPath, compiler: this});
currentNode.convert = result.ast;
for (let i = 0;i < result.dependencies.length;i++) {
let dependPath = result.dependencies[i];
let dependNode = self.createNode({
realPath: dependPath,
nodeType: self.nodeType.Module
})
currentNode.dependencies.push(dependNode);
}
}
async compileJson(currentNode) {
let {cmlType, projectRoot} = this.options;
let jsonObject;
try {
jsonObject = JSON.parse(currentNode.source);
} catch (e) {
this.log.warn(`The .json file corresponding to :${currentNode.realPath} is not correct`);
}
jsonObject = jsonObject || {};
if (currentNode.ext === '.cml') {
let targetObject = jsonObject[cmlType] || {};
if (jsonObject.base) {
targetObject = cmlUtils.merge(jsonObject.base, targetObject)
}
currentNode.convert = targetObject;
targetObject.usingComponents = targetObject.usingComponents || {};
Object.keys(targetObject.usingComponents).forEach(key => {
let comPath = targetObject.usingComponents[key];
let { filePath } = cmlUtils.handleComponentUrl(projectRoot, cmlUtils.delQueryPath(currentNode.realPath), comPath, cmlType);
if (cmlUtils.isFile(filePath)) {
let nodeType = this.nodeType.Module;
if (path.extname(filePath) === '.cml') {
nodeType = this.nodeType.Component;
}
let subNode = this.createNode({
realPath: filePath,
nodeType
})
currentNode.parent.dependencies.push(subNode);
this.compileQueue.push(subNode);
} else {
this.log.error(`can't find component:${comPath} in ${currentNode.realPath} `);
}
})
} else if (currentNode.ext == '.json') {
currentNode.convert = jsonObject;
}
}
async compileAsset(currentNode) {
let realPath = currentNode.realPath;
if (cmlUtils.isInline(realPath)) {
currentNode.output = `module.exports = ${JSON.stringify(this.getPublicPath(realPath)
)}`
}
}
// 用户想要添加文件依赖触发watch重新编译 需要给node添加依赖createNode创建节点
createNode({realPath, source, nodeType, moduleType}) {
this.fileDependencies.add(cmlUtils.delQueryPath(realPath));
let attrs;
if (nodeType === this.nodeType.Module) {
if (!moduleType) {
this.moduleRule.forEach(rule => {
if (rule.test.test(realPath)) {
moduleType = rule.moduleType;
attrs = rule.attrs;
}
})
moduleType = moduleType || this.moduleType.other;
}
} else {
moduleType = null;
}
let key = moduleType === null ? `${nodeType}_${realPath}` : `${nodeType}_${moduleType}_${realPath}`;
/*
缓存判断 1 同一个文件节点不重复创建 compileNode时 compiled为true
有文件 并且mtime 都相同 证明依赖的文件都没有改动 否则删除缓存
*/
if (this.graphNodeMap[key]) {
// 如果是cml文件的children则判断父节点是否有变化, 因为json节点的编译会给父节点添加dependencies,所以父节点变化了子节点也要重新编译
let targetNode = this.graphNodeMap[key].parent ? this.graphNodeMap[key].parent : this.graphNodeMap[key];
if (targetNode.notChange(this.fileTimestamps)) {
let fileDeps = targetNode.getDependenciesFilePaths();
fileDeps.forEach(item => this.fileDependencies.add(item));
return this.graphNodeMap[key];
}
}
let newNode = new CMLNode({
realPath,
nodeType,
moduleType,
attrs,
identifier: key
})
// js模块的模块类型
if (~[this.moduleType.script, this.moduleType.asset].indexOf(moduleType)) {
let modId = this.createModId(realPath);
newNode.modId = modId;
}
let noQueryPath = cmlUtils.delQueryPath(realPath);
newNode.mtime = fs.statSync(noQueryPath).mtime.getTime();
newNode.ext = path.extname(noQueryPath);
if (source) {
newNode.source = source;
} else {
this.readNodeSource(newNode);
}
this.graphNodeMap[key] = newNode;
return newNode;
}
createModId(realPath) {
let modId = realPath;
if (~realPath.indexOf(this.options.projectRoot)) {
modId = path.relative(this.options.projectRoot, realPath);
} else if (~realPath.indexOf(this.options.cmlRoot)) {
modId = path.relative(this.options.cmlRoot, realPath);
}
return modId;
}
// 根据资源路径 返回base64或者publicPath
getPublicPath(filePath) {
let publicPath = this.options.config.output.publicPath;
let mimetype = mime.getType(filePath);
let buf = this.readFileSync(cmlUtils.delQueryPath(filePath));
let result = '';
if (cmlUtils.isInline(filePath)) {
result = `data:${mimetype || ''};base64,${buf.toString('base64')}`
} else {
if (typeof publicPath === 'function') {
return publicPath(filePath);
} else {
// let modId = this.createModId(filePath);
let hash = cmlUtils.createMd5(buf);
if (publicPath[publicPath.length - 1] !== '/') {
publicPath = publicPath + '/';
}
let assetsPath = this.assetsPath;
if (assetsPath[0] === '/') {
assetsPath = assetsPath.slice(1);
}
let splitName = cmlUtils.splitFileName(filePath);
result = publicPath + assetsPath;
let replaceMap = {
filename: splitName[0],
ext: splitName[1],
hash
}
result = result.replace(/\$\{(.*?)\}/g, function(all, $1) {
return replaceMap[$1];
})
return result;
}
}
}
// 开启用户自定义编译
async customCompile() {
// 队列串行编译
// while (this.oneLoopCompiledNode.length) {
// let currentNode = this.oneLoopCompiledNode.shift();
// let key = currentNode.moduleType === null ? `compile-${currentNode.nodeType}` : `compile-${currentNode.nodeType}-${currentNode.moduleType}`;
// this.emit(key, currentNode);
// // AMD模块包装
// if (~[this.moduleType.script, this.moduleType.asset].indexOf(currentNode.moduleType) && currentNode.jsType === 'AMD') {
// AMDWrapper({compiler: this, currentNode})
// }
// }
// 递归编译
this.customCompileNode(this.projectGraph);
}
customCompileNode(currentNode) {
// 存在这个节点
let index = this.oneLoopCompiledNode.indexOf(currentNode);
if (index !== -1) {
// console.log('custom compile' + currentNode.moduleType + currentNode.realPath)
// 先删除 保证只编译一次
this.oneLoopCompiledNode.splice(index, 1);
if (~[this.nodeType.App, this.nodeType.Page, this.nodeType.Component].indexOf(currentNode.nodeType)) {
this.log.debug('custom compile preCML:' + currentNode.nodeType + '_' + currentNode.realPath);
this.emit(`compile-preCML`, currentNode, currentNode.nodeType);
} else {
// Template Script Style Json
this.log.debug('custom compile ' + currentNode.moduleType + ':' + currentNode.realPath);
let parent = currentNode.parent || {};
this.emit(`compile-${currentNode.moduleType}`, currentNode, parent.nodeType);
// AMD模块包装
// if (~[this.moduleType.script, this.moduleType.asset].indexOf(currentNode.moduleType) && currentNode.jsType === 'AMD') {
// AMDWrapper({compiler: this, currentNode})
// }
}
currentNode.childrens.forEach(item => {
this.customCompileNode(item);
})
currentNode.dependencies.forEach(item => {
this.customCompileNode(item);
})
if (~[this.nodeType.App, this.nodeType.Page, this.nodeType.Component].indexOf(currentNode.nodeType)) {
this.log.debug('custom compile postCML:' + currentNode.nodeType + '_' + currentNode.realPath);
this.emit(`compile-postCML`, currentNode, currentNode.nodeType);
}
}
}
utf8BufferToString(buf) {
var str = buf.toString("utf-8");
if (str.charCodeAt(0) === 0xFEFF) {
return str.substr(1);
} else {
return str;
}
}
}
module.exports = Compile;
exports.getAllCML = function({projectRoot}) {
let projectPath = path.resolve(root, 'src');
entry.app = path.join(projectPath, 'app/app.cml');
}
const EventEmitter = require('events');
module.exports = EventEmitter;
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const NodeWatchFileSystem = require("./NodeWatchFileSystem");
const NodeOutputFileSystem = require("./NodeOutputFileSystem");
const NodeJsInputFileSystem = require("enhanced-resolve/lib/NodeJsInputFileSystem");
const CachedInputFileSystem = require("enhanced-resolve/lib/CachedInputFileSystem");
class NodeEnvironmentPlugin {
apply(compiler) {
compiler.inputFileSystem = new CachedInputFileSystem(new NodeJsInputFileSystem(), 60000);
const inputFileSystem = compiler.inputFileSystem;
compiler.outputFileSystem = new NodeOutputFileSystem();
compiler.watchFileSystem = new NodeWatchFileSystem(compiler.inputFileSystem);
compiler.hook("before-run", () => {
if (compiler.inputFileSystem === inputFileSystem) {inputFileSystem.purge();}
callback();
});
}
}
module.exports = NodeEnvironmentPlugin;
const fs = require("fs");
const path = require("path");
const mkdirp = require("mkdirp");
class NodeOutputFileSystem {
constructor() {
this.mkdirp = mkdirp;
this.mkdir = fs.mkdir.bind(fs);
this.rmdir = fs.rmdir.bind(fs);
this.unlink = fs.unlink.bind(fs);
this.writeFile = fs.writeFile.bind(fs);
this.join = path.join.bind(path);
}
}
module.exports = NodeOutputFileSystem;
// fileSystem.utf8BufferToString = function (buf) {
// var str = buf.toString("utf-8");
// if (str.charCodeAt(0) === 0xFEFF) {
// return str.substr(1);
// } else {
// return str;
// }
// }
// fileSystem.convertArgs = function(args, raw) {
// if (!raw && Buffer.isBuffer(args[0])) {args[0] = utf8BufferToString(args[0]);} else if (raw && typeof args[0] === "string")
// {args[0] = new Buffer(args[0], "utf-8");} // eslint-disable-line
// }
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const Watchpack = require("watchpack");
class NodeWatchFileSystem {
constructor(inputFileSystem) {
this.inputFileSystem = inputFileSystem;
this.watcherOptions = {
aggregateTimeout: 0
};
this.watcher = new Watchpack(this.watcherOptions);
}
watch(files, dirs, missing, startTime, options, callback, callbackUndelayed) {
if (!Array.isArray(files)) {throw new Error("Invalid arguments: 'files'");}
if (!Array.isArray(dirs)) {throw new Error("Invalid arguments: 'dirs'");}
if (!Array.isArray(missing)) {throw new Error("Invalid arguments: 'missing'");}
if (typeof callback !== "function") {throw new Error("Invalid arguments: 'callback'");}
if (typeof startTime !== "number" && startTime) {throw new Error("Invalid arguments: 'startTime'");}
if (typeof options !== "object") {throw new Error("Invalid arguments: 'options'");}
if (typeof callbackUndelayed !== "function" && callbackUndelayed) {throw new Error("Invalid arguments: 'callbackUndelayed'");}
const oldWatcher = this.watcher;
this.watcher = new Watchpack(options);
if (callbackUndelayed) {this.watcher.once("change", callbackUndelayed);}
this.watcher.once("aggregated", (changes, removals) => {
changes = changes.concat(removals);
if (this.inputFileSystem && this.inputFileSystem.purge) {
this.inputFileSystem.purge(changes);
}
const times = this.watcher.getTimes();
callback(null,
changes.filter(file => files.indexOf(file) >= 0).sort(),
changes.filter(file => dirs.indexOf(file) >= 0).sort(),
changes.filter(file => missing.indexOf(file) >= 0).sort(), times, times);
});
this.watcher.watch(files.concat(missing), dirs.concat(missing), startTime);
if (oldWatcher) {
oldWatcher.close();
}
return {
close: () => {
if (this.watcher) {
this.watcher.close();
this.watcher = null;
}
},
pause: () => {
if (this.watcher) {
this.watcher.pause();
}
}
};
}
}
module.exports = NodeWatchFileSystem;
const chalk = require('chalk');
class Log {
constructor(options = {}) {
this.level = options.level || 2;
}
debug (msg) {
if (this.level >= 3) {
process.stdout.write('\n' + chalk.gray('[DEBUG]') + ' ' + msg + '\n');
}
}
notice (msg) {
if (this.level >= 2) {
process.stdout.write('\n' + chalk.cyan('[INFO]') + ' ' + msg + '\n');
}
}
warn (msg) {
if (this.level >= 1) {
process.stdout.write('\n' + chalk.yellow('[WARNI]') + ' ' + msg + '\n');
}
}
error(msg) {
if (this.level >= 0) {
process.stdout.write('\n' + chalk.red('[ERROR]') + ' ' + msg + '\n');
}
}
}
module.exports = Log;
const Compile = require('./Compile');
/**
* @param {options} 构建参数
* @return {Compile} 编译对象
*/
function MVVMPack(options) {
let compile = new Compile(options);
return compile;
}
module.exports = MVVMPack;
module.exports = function({compiler, currentNode}) {
currentNode.output = `define('${currentNode.modId}', function(require, exports, module){${currentNode.output}\r\n});`
}
let AMDWrapper = require('./AMDWrapper');
module.exports = {
AMDWrapper
}
const { ResolverFactory, NodeJsInputFileSystem, CachedInputFileSystem} = require('enhanced-resolve');
const path = require('path');
module.exports = function(options) {
let defaultOptions = {
extensions: ['.cml', '.interface', '.vue', '.js', '.json'],
alias: {
'$CMLPROJECT': path.join(options.cmlRoot),
'$PROJECT': path.join(options.projectRoot)
},
modules: [
'node_modules',
path.join(options.cmlRoot, '/node_modules')
],
"unsafeCache": true,
"mainFiles": ["index"],
"aliasFields": ["browser"],
"mainFields": ["browser", "module", "main"],
"cacheWithContext": false}
options.config = options.config || {}
options.config.resolve = options.config.resolve || {};
return ResolverFactory.createResolver(Object.assign(
{
useSyncFileSystemCalls: true,
fileSystem: new CachedInputFileSystem(new NodeJsInputFileSystem(), 4000)
},
defaultOptions,
options.config.resolve
))
}
class Watching {
constructor(compiler, watchOptions, handler) {
this.startTime = null;
this.invalid = false;
this.handler = handler;
this.callbacks = [];
this.closed = false;
if (typeof watchOptions === "number") {
this.watchOptions = {
aggregateTimeout: watchOptions
};
} else if (watchOptions && typeof watchOptions === "object") {
this.watchOptions = Object.assign({}, watchOptions);
} else {
this.watchOptions = {};
}
this.watchOptions.aggregateTimeout = this.watchOptions.aggregateTimeout || 200;
this.compiler = compiler;
this.running = true;
this._go();
}
async _go() {
// 记录上一次编译开始的时间 下次watch从这时候开始
this.startTime = Date.now();
this.running = true;
this.invalid = false;
await this.compiler.run();
this._done();
}
_getStats(compilation) {
const stats = new Stats(compilation);
stats.startTime = this.startTime;
stats.endTime = Date.now();
return stats;
}
_done(err, compilation) {
this.running = false;
if (this.invalid) {return this._go();}
this.handler(null, this.compiler);
let contextDependencies = [];
let missingDependencies = [];
if (!this.closed) {
this.watch([...this.compiler.fileDependencies], contextDependencies, missingDependencies);
}
}
watch(files, dirs, missing) {
this.pausedWatcher = null;
this.watcher = this.compiler.watchFileSystem.watch(files, dirs, missing, this.startTime, this.watchOptions, (err, filesModified, contextModified, missingModified, fileTimestamps, contextTimestamps) => {
this.pausedWatcher = this.watcher;
this.watcher = null;
if (err) {return this.handler(err);}
this.compiler.fileTimestamps = fileTimestamps;
this.compiler.contextTimestamps = contextTimestamps;
this.invalidate();
}, (fileName, changeTime) => {
this.compiler.emit("invalid", fileName, changeTime);
});
}
invalidate(callback) {
if (callback) {
this.callbacks.push(callback);
}
if (this.watcher) {
this.pausedWatcher = this.watcher;
this.watcher.pause();
this.watcher = null;
}
if (this.running) {
this.invalid = true;
return false;
} else {
this._go();
}
}
close(callback) {
if (callback === undefined) {callback = function() {};}
this.closed = true;
if (this.watcher) {
this.watcher.close();
this.watcher = null;
}
if (this.pausedWatcher) {
this.pausedWatcher.close();
this.pausedWatcher = null;
}
if (this.running) {
this.invalid = true;
this._done = () => {
this.compiler.emit("watch-close");
callback();
};
} else {
this.compiler.emit("watch-close");
callback();
}
}
}
module.exports = Watching;
{
"name": "old-mvvm-pack",
"version": "0.3.0-alpha.2",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"chai": "^4.2.0",
"chalk": "^2.4.2",
"chameleon-tool-utils": "0.3.0-alpha.2",
"enhanced-resolve": "^4.1.0",
"mime": "^2.4.0",
"mvvm-interface-parser": "0.3.0-alpha.2",
"mvvm-script-parser": "0.3.0-alpha.2",
"mvvm-style-parser": "0.3.0-alpha.2",
"mvvm-template-parser": "0.3.0-alpha.2",
"watchpack": "^1.6.0"
}
}
\ No newline at end of file
const filePath = path.resolve('./ResolveFactory.test.js')
const result = compile.inputFileSystem.readFileSync(filePath);
const Compile = require('../lib/Compile.js');
const path = require('path');
let compile = new Compile({
config: {},
cmlRoot: path.resolve('./'),
projectRoot: path.resolve('./')
})
const filePath = path.resolve('./ResolveFactory.test.js')
const result = compile.readFileSync(filePath);
const path = require('path')
const ResolveFactory = require('../lib/ResolveFactory.js')
let resolve = ResolveFactory({
config: {},
cmlRoot: path.resolve('../'),
projectRoot: path.join('../')
});
resolve.resolve({}, '/Users/didi/Documents/newcml/open/mvvm-sets/test_project/src/app/', '../router.config.json?__inline', {}, function(err, result) {
if (err) {
throw err;
}
console.log(path.extname(result))
// expect(result).to.equal(path.join(__dirname,'../src/Event.js'))
})
const ResolveFactory = require('../lib/ResolveFactory.js')
let expect = require('chai').expect;
const path = require('path');
describe('ResolveFactory', function() {
it('getResolveObject', function() {
let result = ResolveFactory({
config: {},
cmlRoot: path.resolve('../'),
projectRoot: path.join('../')
});
expect(typeof result).to.equal('object')
})
it('resolve Relative Path', function() {
let resolve = ResolveFactory({
config: {},
cmlRoot: path.resolve('../'),
projectRoot: path.join('../')
});
resolve.resolve({}, path.join(__dirname), '../src/Event', {}, function(err, result) {
if (err) {
throw err;
}
console.log(result)
expect(result).to.equal(path.join(__dirname, '../src/Event.js'))
})
})
})
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册