-
46 | ${dirHTML.genDirList(data.dirs)}
47 | ${dirHTML.genFileList(data.files)}
48 |
├── .editorconfig ├── .gitignore ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── README.md ├── bin └── jdf ├── doc ├── a_tool_api.md ├── a_tool_build.md ├── a_tool_command.md ├── a_tool_config.md ├── a_tool_csssprite.md ├── a_tool_develop.md ├── a_tool_format.md ├── a_tool_lint.md ├── a_tool_server.md ├── core_css_optimize.md ├── core_plugin.md ├── core_smarty.md ├── core_tpl.md ├── core_vm.md ├── core_widget.md └── core_widgetoutputname.md ├── index.js ├── lib ├── VFS │ ├── VirtualFile.js │ ├── VirtualFileSystem.js │ └── fileType.js ├── base64.js ├── build.js ├── buildCss.js ├── buildES6.js ├── buildHTML.js ├── buildHTMLDeep.js ├── buildOutputWidget.js ├── buildWidget.js ├── compresser │ ├── compress.js │ ├── compressScheduler.js │ ├── compressWorker.js │ ├── minifyCss.js │ ├── minifyHtml.js │ ├── minifyImage.js │ └── minifyJs.js ├── concat.js ├── config.js ├── cssSprite.js ├── fileFormat.js ├── fileLint.js ├── htmlAst │ ├── index.js │ ├── nodeHandler.js │ └── walk.js ├── install │ ├── componentsData.js │ └── index.js ├── jdf.js ├── jsAst │ ├── index.js │ ├── seajsReplace.js │ └── typeCheck.js ├── output.js ├── pluginCore │ └── index.js ├── server │ ├── browserSyncServer.js │ ├── genPort.js │ ├── injector │ │ ├── dumpSeajsCombo.js │ │ └── index.js │ ├── middlewareLocal.js │ ├── middlewareVFS │ │ ├── envConfig.js │ │ ├── index.js │ │ ├── middlewareVFS.js │ │ ├── res.404.js │ │ ├── res.comboContent.js │ │ ├── res.dirView │ │ │ ├── dir.css.js │ │ │ ├── dir.html.js │ │ │ ├── iconfont.eot │ │ │ ├── iconfont.svg │ │ │ ├── iconfont.ttf │ │ │ ├── iconfont.woff │ │ │ ├── index.js │ │ │ └── res.dirView.js │ │ ├── res.file.js │ │ ├── tpl.footer.js │ │ └── view.js │ ├── mime.js │ └── openurl.js ├── urlReplace.js ├── vm.js └── widget.js ├── package.json ├── template ├── config.json ├── css │ └── style.scss └── html │ └── index.html └── test ├── buildOutputWidget.js ├── buildWidget.js ├── buildcss.js ├── config ├── config.json └── index.js ├── index.js ├── urlReplace ├── comboUrlPath │ ├── case01.html │ └── result01.html └── index.js └── vfs ├── files ├── QR.jpg ├── css.css ├── doc.docx ├── es6.js ├── importless.less ├── js.js ├── less.less ├── sass.scss └── text.txt └── virtual-file.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.js] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | .svn 4 | .idea 5 | .DS_Store 6 | .vscode 7 | package-lock.json 8 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | .svn 4 | .DS_Store 5 | test 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "4.4.5" 4 | - "5" 5 | - "6" 6 | - "8" 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [](https://nodei.co/npm/jdfx/) 2 | 3 | [](http://badge.fury.io/js/jdfx) [](https://travis-ci.org/jdf2e/jdf) 4 | 5 | ## 关于JDFX 6 | 7 | JDFX是京东前端团队自主研发的一款自动化构建工具,目的是合理、快速和高效的解决前端开发中的工程和项目问题,核心集成了本地调试、本地构建、远程布署、代码生成等一系列开发命令工具。 8 | 9 | ## 安装使用 10 | 11 | * [nodejs@4.4.5到最新LTS版本可用](http://nodejs.org/) 12 | ``` 13 | $ npm install jdfx -g 14 | ``` 15 | * 执行`jdf -V`,测试是否安装成功(注意是大写的`V`) 16 | 17 | * [快速开始](https://github.com/jdf2e/jdf/blob/master/doc/a_tool_develop.md) 18 | 19 | * [配置config.json](https://github.com/jdf2e/jdf/blob/master/doc/a_tool_config.md) 20 | 21 | ## 更新日志 22 | 23 | [完整日志](https://github.com/jdf2e/jdf/blob/master/CHANGELOG.md) 24 | 25 | ### 3.4.13 / 2020/11/20 18:34:00 26 | * [fix] 修改node-sass版本,修复node-sass更新到5.0.0后无法在node 8安装的问题。 27 | 28 | ## 说明文档 29 | * [示例安装](https://github.com/jdf2e/jdf/blob/master/doc/a_tool_develop.md) 30 | * [命令文档](https://github.com/jdf2e/jdf/blob/master/doc/a_tool_command.md) 31 | * [配置文档](https://github.com/jdf2e/jdf/blob/master/doc/a_tool_config.md) 32 | * [api调用文档](https://github.com/jdf2e/jdf/blob/master/doc/a_tool_api.md) 33 | * [css优化策略](https://github.com/jdf2e/jdf/blob/master/doc/core_css_optimize.md) 34 | * [css雪碧图](https://github.com/jdf2e/jdf/blob/master/doc/a_tool_csssprite.md) 35 | * [smarty模版](https://github.com/jdf2e/jdf/blob/master/doc/core_smarty.md) 36 | * [tpl模版](https://github.com/jdf2e/jdf/blob/master/doc/core_tpl.md) 37 | * [vm模版](https://github.com/jdf2e/jdf/blob/master/doc/core_vm.md) 38 | * [widget说明](https://github.com/jdf2e/jdf/blob/master/doc/core_widget.md) 39 | * [widgetOutputName标签](https://github.com/jdf2e/jdf/blob/master/doc/core_widgetoutputname.md) 40 | * [插件模块](https://github.com/jdf2e/jdf/blob/master/doc/core_plugin.md) 41 | 42 | ## 使用攻略 43 | * [文件路径拼写说明](https://github.com/jdf2e/jdf/issues/6) 44 | * [移动端页面开发](https://github.com/jdf2e/jdf/issues/7) 45 | 46 | ## 功能介绍 47 | 48 | #### 跨平台 49 | * 完美支持windows、mac、linux三大系统 50 | 51 | #### 项目构建 52 | * 生成标准化的项目文件夹 53 | * 支持本地联调,本地编译,测试预览等开发流程 54 | * 每个项目拥有独立的配置文件,按选项统一编译 55 | 56 | #### 模块开发 57 | * 可快速方便的对模块进行创建,引用,预览,安装和发布 58 | * 通过积累,可形成完全符合自己业务的模块云服务 59 | * 支持将vm和smarty模版编译为html 60 | * 支持将sass和less编译为css 61 | * 支持velocity语法 62 | * 支持ES6(需要将js文件后缀改为`.babel`) 63 | 64 | #### 项目输出 65 | * 自动将页面中的js、css引用转换成combo请求格式 66 | * 自动压缩优化js、css、图片文件 67 | * 默认给所有静态资源添加CDN域名 68 | * 支持cmd规范,自动提取文件id和dependencies,压缩时保留require关键字 69 | * 自动生成css精灵图,并更新background-position属性值 70 | * 自动生成base64编码 71 | * 自动给css样式添加相应的浏览器前缀 72 | * 支持图片生成webp格式,并更新相关css图片链接 73 | * 压缩css、js、图片文件,并且可根据当前项目中的文件数量自动决定是否启用多线程进行压缩,当前的数量阀值是`200` 74 | * 自动给js,css文件的内容头部添加时间戳,方便查看 75 | * 文件编码统一输出为utf8 76 | 77 | #### 项目联调 78 | * 一键上传文件到测试服务器,方便其他同学开发预览 79 | 80 | #### 本地服务 81 | * 支持开启本地服务器,方便调试 82 | * 支持本地静态文件预览,内置本地开发调试服务器,以及当前目录浏览 83 | * 支持实时监听文件,文件被修改时会自动编译成css,并刷新浏览器 84 | * 内置browserSync 85 | * [详细文档](https://github.com/jdf2e/jdf/blob/master/doc/a_tool_build.md) 86 | 87 | #### 辅助工具 88 | * 支持html/js/css文件格式化 89 | * 支持html/js/css代码压缩 90 | * 支持html/js/css文件lint,代码质量检查 91 | 92 | ## 集成工具 93 | 94 | * [在本地任意目录开启一个server静态服务器](https://github.com/jdf2e/jdf/blob/master/doc/a_tool_server.md) 95 | * [html/js/css文件lint代码质量检查](https://github.com/jdf2e/jdf/blob/master/doc/a_tool_lint.md) 96 | * [html/js/css文件格式化](https://github.com/jdf2e/jdf/blob/master/doc/a_tool_format.md) 97 | * [csssprite图片合并](https://github.com/jdf2e/jdf/blob/master/doc/a_tool_csssprite.md) 98 | 99 | ## widget组件 100 | 101 | * 详情请参考[widget文档](https://github.com/jdf2e/jdf/blob/master/doc/core_widget.md) 102 | * [vm模版文档](https://github.com/jdf2e/jdf/blob/master/doc/core_vm.md) 103 | * [tpl模版文档](https://github.com/jdf2e/jdf/blob/master/doc/core_tpl.md) 104 | * [smarty模版文档](https://github.com/jdf2e/jdf/blob/master/doc/core_smarty.md) 105 | 106 | ## 编译器插件 107 | * [Sublime Text2 插件](https://sublime.wbond.net/packages/Jdf%20-%20Tool) 108 | 109 | -------------------------------------------------------------------------------- /bin/jdf: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | process.stdout.isTTY = true; 3 | process.stderr.isTTY = true; 4 | require('../index.js').init(process.argv); -------------------------------------------------------------------------------- /doc/a_tool_api.md: -------------------------------------------------------------------------------- 1 | # api文档 2 | 3 | ## 安装使用 4 | 5 | ```javascript 6 | npm install jdfx 7 | ``` 8 | 9 | ```javascript 10 | const jdfx = require('jdfx'); 11 | 12 | jdfx.build(port => { 13 | console.log(port); 14 | }); 15 | ``` 16 | 17 | ## 方法说明 18 | 19 | ### jdf.setConfig([options]) 20 | 21 | 设置jdf的全局运行参数 22 | 23 | ```javascript 24 | const jdfx = require('jdfx'); 25 | 26 | jdfx.setConfig({ 27 | projectPath: '/Users/chenxiaochun/Documents/MyProject/jdf-test' 28 | }) 29 | ``` 30 | 31 | * `options`,全局参数设置 32 | * `projectPath`,指定项目运行的绝对路径,默认为当前目录 33 | 34 | ### jdf.server([options, callback]) 35 | 36 | ```javascript 37 | const jdfx = require('jdfx'); 38 | 39 | jdfx.server(() => { 40 | console.log('server is ok'); 41 | }); 42 | ``` 43 | 44 | * `options`,当前服务配置 45 | * `open`,是否自动在浏览器中打开当前服务,默认为`false` 46 | * `watch`,是否实时监听当前项目的文件变动,默认为`false` 47 | * `callback`,服务启动之后的回调函数 48 | 49 | ### jdf.build([options, callback]) 50 | 51 | ```javascript 52 | const jdfx = require('jdfx'); 53 | 54 | jdfx.build(port => { 55 | console.log(port); 56 | }); 57 | ``` 58 | 59 | * `options`,当前服务配置 60 | * `open`,是否自动在浏览器中打开当前服务,默认为`false` 61 | * `callback`,服务启动之后的回调函数,当前参数为服务的端口号 62 | 63 | ### jdf.output([dir, options]) 64 | 65 | ```javascript 66 | const jdfx = require('jdfx'); 67 | 68 | jdfx.output(); 69 | ``` 70 | 71 | * `dir`,指定需要单独输出的目录,类型为数组 72 | * `options`,输出时的类型配置 73 | * `debug`,输出时不压缩文件,不对html文件中引用的资源进行combo,默认为`false` 74 | * `plain`,只对项目进行编译,不压缩文件,不对html文件中引用的资源进行combo,默认为`false` 75 | 76 | ### jdf.upload([dir, options]) 77 | 78 | 上传的方法内部会默认调用`jdf.output()`方法,因此,不需要在上传之前单独调用输出方法 79 | 80 | ```javascript 81 | const jdfx = require('jdfx'); 82 | 83 | jdfx.upload(); 84 | ``` 85 | 86 | * `dir`,想要单独上传的文件目录,类型为数组 87 | * `options`,上传时的配置参数 88 | * `type`,指定上传方式:`ftp|scp|http`,默认为`http` 89 | * `debug`,上传时不压缩文件,不对html文件中引用的资源进行combo,默认为`false` 90 | * `plain`,只对项目进行编译,不压缩文件,不对html文件中引用的资源进行combo,默认为`false` 91 | * `preview`,上传模版文件到服务器,默认为`false` 92 | 93 | ### jdf.clean() 94 | 95 | 清除当前项目的jdf缓存文件 96 | 97 | ### jdf.exit() 98 | 99 | 退出当前项目的jdf服务 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /doc/a_tool_build.md: -------------------------------------------------------------------------------- 1 | # jdf build 2 | 3 | ## 简介 4 | `jdf build | jdf b`命令主要用于编译jdf项目并开启[本地server](a_tool_server.md)来调试项目。 5 | 6 | ## 特点 7 | * 自动刷新 8 | 保存文件自动编译并刷新浏览器,无需安装任何插件; 9 | * 多终端同步 10 | 多终端同步响应鼠标键盘动作,实时查看多终端对同一操作的效果; 11 | * 导航 12 | 浏览器中显示项目文件夹列表,访问目标页面无需在浏览器输出访问路径; 13 | * 移动端调试 14 | jdf集成weinre工具,通过weinre,可以调试移动设备中页面; 15 | ## 命令参数 16 | 17 | * `--open` 或 `-o`,在开启本地服务的同时,自动在浏览器中打开根目录文件列表页面 18 | * `--help` 或 `-h`,查看jdf b帮助 19 | 20 | ## 控制台信息 21 | 22 | 编译成功后,控制台中会打印如下信息: 23 | 24 | [JDFX] Access URLs: 25 | -------------------------------------- 26 | Local: http://localhost:80 27 | External: http://192.168.191.1:80 28 | -------------------------------------- 29 | UI: http://localhost:3001 30 | UI External: http://192.168.191.1:3001 31 | -------------------------------------- 32 | 33 | * `Local`,本地服务器地址 34 | * `External`,同网段内其他机器访问地址,用于移动端访问 35 | * `UI`,jdf服务器控制面板地址,从这个入口可开启weinre,模拟网络限流等功能 36 | * `UI External`,同网段内访问服务器控制面板地址 37 | 38 | 39 | ## 使用方法 40 | 41 | ### PC端 42 | * 用`jdf init xxx`创建jdf工程后,在控制台输入`jdf b -o`后保留这个控制台运行,方便实时调试; 43 | * 在自动打开的浏览器页面中点击导航到开发页面,编辑widget等文件,查看浏览器的同步改动; 44 | * 引用mock来模拟数据生成和读取,建议[mock](https://github.com/nuysoft/Mock)。 45 | * 去除mock引用,[切换hosts](https://github.com/oldj/SwitchHosts),模拟线上环境。 46 | 47 | ### M端 48 | * 将移动设备与开发电脑连接到同一网段内; 49 | * 在控制台输入`jdf b -o`,保留这个控制台; 50 | * 用移动设备扫描浏览器弹出页面上的二维码或者在移动设备浏览器上输入`External`的网址; 51 | * 在电脑或手机上导航到开发页面,编辑widget等文件,查看移动设备和开发电脑上浏览器的页面改动; 52 | 53 | ### M端调试 54 | * 在`jdf b -o`运行时,可以通过修改本地文件实时查看M端页面改动; 55 | * 如果需要微调,则可以使用集成的weinre,如何使用请参见[weinre说明](a_tool_weinre.md); 56 | 57 | ## 常见问题 58 | * 终端报acron.js错误 59 | - 请检查js有无写错 60 | - html中的模板由` 75 | 76 | ``` 77 | 进行combo之后为: 78 | ```html 79 | 80 | ``` 81 | 82 | * 生成精灵图[cssSprite](a_tool_csssprite.md) 83 | * 生成base64编码 84 | 85 | --- 86 | 87 | * `-d`或者`--debug`,以debug的模式输出当前项目,不压缩项目中的任何文件 88 | * `-v`或者`--verbose`,将会详细输出当前项目每一个文件的编译信息。此参数特别适用于输出时卡死的情况,可以方便的查看问题出在了哪一个文件上 89 | * `-p`或者`--plain`,只编译widget、less、scss,不做任何其它处理。此模式适用于页面是由前端开发,然后需要把页面交付给后端的同学来完成剩下的工作 90 | * 支持输出指定的文件夹,例如:`jdf o js` 91 | * 支持输出指定的文件,例如:`jdf o js/a.js` 92 | * 支持简单的通配符,例如:`jdf o js/**/*.js`,将只输出`js`文件夹下所有的js文件 93 | 94 | ## jdf upload 95 | 96 | 把当前项目上传到测试服务器 97 | 98 | * 可简写为`jdf u` 99 | * 目前支持三种上传模式:ftp,sftp,http 100 | * `-d`或者`--debug`,以debug的模式输出当前项目,不压缩项目中的任何文件 101 | * `-p`或者`--plain`,只编译widget、less、scss,不做任何其它处理。此模式适用于前端同学仅仅做静态页面,然后把页面交付给后端的同学来完成剩下的工作 102 | * `-P`或者`--preview`,将当前项目上传到`previewServerDir`配置的目录之下 103 | * `-v`或者`--verbose`,将会详细输出当前项目每一个文件的编译信息。此参数特别适用于上传时卡死的情况,可以方便的查看问题出在了哪一个文件上 104 | * 支持上传指定的文件夹,例如:`jdf u js` 105 | * 支持上传指定的文件,例如:`jdf u js/a.js` 106 | * 支持简单的通配符,例如:`jdf u js/**/*.js`,将只输出`js`文件夹下所有的js文件 107 | 108 | ## jdf widget 109 | 110 | * 可简写为`jdf w` 111 | * `-s` 或者 `--smarty`,创建widget时以smarty模板形式创建 112 | * `-c xxx`或者`--create xxx`,创建一个widget 113 | * `-P xxx`或者`--preview [xxx]`,预览指定的widget,当没有指定widget时,预览全部widget 114 | * `-l xxx`或者`--list`,获取服务器上所有的widget列表 115 | * `-p xxx`或者`--publish xxx`,发布widgt到服务器上 116 | * `-i xxx`或者`--install xxx`,安装指定的widget到当前项目 117 | 118 | ## jdf lint 119 | 120 | html、css、js文件代码质量检查工具,详细用法可点击[这里](a_tool_lint.md) 121 | 122 | * 可简写为`jdf l xxx`,后面跟指定的文件夹/文件 123 | * [详细说明](a_tool_lint.md) 124 | 125 | ## jdf format 126 | 127 | html、css、js文件格式化工具,详细用法可点击[这里](a_tool_format.md) 128 | 129 | * 可简写为`jdf f xxx`,后面跟指定的文件夹/文件 130 | * [详细说明](a_tool_format.md) 131 | 132 | ## jdf compress 133 | 134 | html、css、js文件压缩工具,详细用法可点击[这里](a_tool_deploy.md) 135 | 136 | * 可简写为`jdf c xxx`,后面跟指定的文件夹/文件 137 | 138 | ## jdf clean 139 | 140 | 清理jdf缓存文件,遇到比较反常的现象时,可尝试执行此命令 141 | 142 | ## jdf -h 143 | 144 | 获取jdf的帮助信息 145 | 146 | ## jdf -V 147 | 148 | 获取jdf的当前版本号,注意是大写的`V` 149 | 150 | ## jdf参数配置文档 151 | 152 | 请参考:[jdf参数配置文档](a_tool_config.md)进行查阅。 153 | 154 | 155 | -------------------------------------------------------------------------------- /doc/a_tool_config.md: -------------------------------------------------------------------------------- 1 | # 配置文档 2 | 3 | 每一个项目的根目录都有一个独立的config.json配置文件,其详细配置如下: 4 | 5 | * `"projectPath": null` - 【常用】工程目录前缀 6 | 7 | * `"cssDir": "css"` - css文件夹名称 8 | 9 | * `"imagesDir": "css/i"` - images文件夹名称 10 | 11 | * `"jsDir": "js"` - js文件夹名称 12 | 13 | * `"htmlDir": "html"` - html文件夹名称 14 | 15 | * `"widgetDir": "widget"` - widget文件夹名称 16 | 17 | * `"outputDirName": "build"` - 输出文件夹名称 18 | 19 | * `"outputCustom": ""` - 自定义输出文件夹,以逗号分隔的字符串 20 | 21 | * `"cdn": "//misc.360buyimg.com"` - 【常用】静态cdn域名 22 | 23 | * `"serverDir": "misc.360buyimg.com"` - 上传至远端服务器文件夹的名称 24 | 25 | * `"previewServerDir": "page.jd.com"` - html文件夹上传至服务器所在的文件夹名称 26 | 27 | * `"widgetServerDir": "jdfwidget.jd.com"` - widget服务器所在的文件夹名称 28 | 29 | * `"widgetOutputName": "widget"` - 全局widgetOutputName名称 30 | 31 | * `"widgetOutputMode": 1` - 编译全局wigetOutputName模式,共三种:1: all widgets|2: white list|3: black list 32 | 33 | * `"widgetWhiteList": []` - 指定白名单,在widgetOutputMode=2时,输出这个列表内容到widget.js/widget.css中 34 | 35 | * `"widgetBlackList": []` - 指定黑名单,在widgetOutputMode=3时,排除这个列表的widget 36 | 37 | * `"widgetNesting": true` - widget嵌套功能开关,默认开启 38 | 39 | * `"localServerPort": 80` - 【常用】本地服务器端口 40 | 41 | * `"build"` 42 | * `"jsPlace": "insertBody"` - 调试时js文件位置 insertHead|insertBody 43 | 44 | * `"livereload":true` - 是否开启liveload 45 | 46 | * `"sass":true` - 是否开启sass编译 47 | 48 | * `"less":true` - 是否开启less编译 49 | 50 | * `"csslint": false` - 是否开启csslint 51 | 52 | * `"upload"` 53 | * `"type": "http"` - 默认 ftp scp http 54 | * `"host": null` - 服务器的域名或者ip 55 | * `"user": null` - 上传时使用的用户名, ftp、scp需要,http不需要 56 | * `"password": null` - 规则同上 57 | * `"port": null` - 传输端口,ftp默认21,scp默认22,http默认3000 58 | * `"rootPrefix": "/var/www/html/"` - scp时传输的目录前缀,用来确认上传文件最终的地址,一个文件最终的地址会是rootPrefix + serverDir + projectPath + filePath,你可以根据自身server的配置来修改这个值 59 | 60 | * `"output"` 61 | * `"cssImagesUrlReplace": true` - css中图片url加cdn替换 62 | 63 | * `"linkReplace": true` - 给link.href添加cdn前缀,v3.3.0版本新增,之前由`jsUrlReplace`参数控制link.href添加cdn 64 | 65 | * `"jsUrlReplace": false` - js文件的id和dependences是否添加cdn前缀 66 | 67 | * `"jsPlace": "insertBody"` - 编译后js文件位置 insertHead|insertBody 68 | 69 | * `"cssCombo": true` - 【常用】css进行combo 70 | 71 | * `"jsCombo": true` - 【常用】js进行combo todo 72 | 73 | * `"hasBanner": true` - 是否给js文件,css文件添加banner时间戳 74 | 75 | * `"compressJs":true` - 【常用】是否开启压缩js文件 76 | 77 | * `"compressCss":true` - 【常用】是否开启压缩css文件 78 | 79 | * `"compressImage":true` - 【常用】是否开启压缩图片 80 | 81 | * `"cssSprite":true` - 是否开启css sprite功能 82 | 83 | * `"cssSpriteMode": 1` - 0: 将所有css文件中的背景图合并成一张sprite图片,1: 将每一个widget中的背景图分别合并成一张图片 84 | 85 | * `"cssSpriteMargin": 10` - css sprite图片之间的间距 86 | 87 | * `"cssSpriteDirection": vertical` - vertical:垂直合并,horizontal:水平合并 88 | 89 | * `"cssAutoPrefixer": true` - 【常用】是否自动删除过时的浏览器css前缀,如`-webkit-box-orient: vertical;` 90 | 91 | * `"browserslist": ["last 2 version", "> 0.2%", "ie > 7"]` - 【常用】配置autoprefixer的browserslist。 92 | 93 | * `"base64": false` - 是否对图片进行base64编码 94 | 95 | * `"webp":false` - 是否生成对应的webp图片 96 | 97 | * `"excludeFiles": ""` - 【常用】想要直接忽略的文件/文件夹,以逗号分隔的字符串:"test,build" 98 | 99 | * `"babel"` - 默认只启用基本转义(3.4.10及之前preset-es2015,之后为preset-env),您也可以在项目根目录下新建一个`.babelrc`文件独立配置 100 | * `"presets": []` 101 | * `"plugins": []` 102 | 103 | 104 | -------------------------------------------------------------------------------- /doc/a_tool_csssprite.md: -------------------------------------------------------------------------------- 1 | # csssprite图片合并 2 | 3 | ## 使用说明 4 | 5 | ### 默认单位为px 6 | 非常简单,只需要在css文件中对要合并的图片路径增加?__sprite后缀即可,比如 7 | ``` css 8 | .csssprite .abtest_huafei s { 9 | background:url(i/icon_01.png?__sprite) no-repeat; 10 | } 11 | .csssprite .abtest_lvxing s { 12 | background:url(i/icon_03.png?__sprite) no-repeat 6px 0px; 13 | } 14 | .csssprite .abtest_caipiao s { 15 | background:url(i/icon_05.png?__sprite) no-repeat 5px 0px; 16 | } 17 | ``` 18 | 19 | 执行jdf output,后台会进行css sprite编译操作后 20 | ``` css 21 | .csssprite .abtest_huafei s { 22 | background:url(i/csssprite.png) no-repeat; 23 | background-position:0 0 24 | } 25 | .csssprite .abtest_lvxing s { 26 | background:url(i/csssprite.png) no-repeat 6px 0; 27 | background-position:6px -39px 28 | } 29 | .csssprite .abtest_caipiao s { 30 | background:url(i/csssprite.png) no-repeat 5px 0; 31 | background-position:5px -78px 32 | } 33 | ``` 34 | 其中`icon_01.png`,`icon_03.png`,`icon_05.png`小图片被合成为`csssprite.png`,其中csssprite为当前css文件的文件名 35 | 36 | ### 当css单位为rem时 37 | 38 | 在background中写上px到rem的转换比例 39 | ``` css 40 | html { 41 | font-size: 20px; 42 | } 43 | .icon1, .icon2{ 44 | width: 1.8rem; 45 | height: 1.8rem; 46 | margin: 10px; 47 | background: url(i/icon7.png?__sprite__rem20) no-repeat; 48 | border: 1px solid black; 49 | } 50 | .icon2{ 51 | background: url(i/icon8.png?__sprite__rem20) no-repeat; 52 | } 53 | ``` 54 | 转换之后: 55 | ```css 56 | html { 57 | font-size: 20px 58 | } 59 | .icon1, .icon2 { 60 | width: 1.8rem; 61 | height: 1.8rem; 62 | margin: 10px; 63 | background: url(/i/w2.png) no-repeat; 64 | background-position: 0 0; 65 | background-size: 1.8rem 4.6rem; 66 | border: 1px solid #000 67 | } 68 | .icon2 { 69 | background: url(i/w2.png) no-repeat; 70 | background-position: 0 -2.3rem; 71 | background-size: 1.8rem 4.6rem 72 | } 73 | ``` 74 | 75 | ## 切图说明 76 | 把psd中图片所有icon类小图切换,在css中设置好background-position,在相对应图片后面增加?__sprite后缀 77 | 78 | ## 配置说明 79 | * 默认为开启状态,可以通过config.json的output.csssprite键值设置为false进行关闭 80 | * 图片之间上下间距,可以通过config.json的output.cssspriteMargin键值设置 81 | * 合并文件,通过config.json的outout.cssSpriteMode 0|1 设置把整个项目的雪碧图合并到一起,还是以widget为单位合并到一起,默认是1。 82 | 83 | ## 特性说明 84 | * 支持的图片格式:png,jpg,png输出png24格式,IE6的png24图片需要单独处理 85 | * 支持no-repeat,background-position可自由设置 86 | * 后续支持repeat-x,repeat-y 87 | 88 | ## 原理解析 89 | * 分析css文件内容,取出带有?__sprite的图片路径,同时对此background的backgroud-repeat、background-position进行记录 90 | * 取出所有图片,依靠backgroud-repeat、background-position进行图片合并,并生成合并的新图片 91 | * 把css文件所有sprite图片路径替换成合并的新图片路径 92 | 93 | ## 解析css 94 | * 利用正则实现一个简单的css语法解析器,可把css内容解析为 95 | 96 |
属性 | 值 | 说明 |
content | 图片内容 | |
url | 如:i/icon.png | 图片url |
item | 如:background: url(i/icon.png?__sprite) no-repeat | 图片background |
repeat | null | repeat-x | repeat-y | 重复 |
width | number | 图片的宽度 |
height | number | 图片的高度 |
welcome FEer
71 |hope you enjoy jdf!
72 | ``` 73 | 74 | * 在`myWidget.scss`中输入: 75 | 76 | ``` 77 | .p1 { 78 | font-size: 18px; 79 | span { 80 | color: blue; 81 | } 82 | } 83 | .p2 { 84 | color: red; 85 | } 86 | ``` 87 | 88 | #### 启动开发调试模式 89 | 欢迎进入欢快的开发阶段! 90 | 91 | * 运行`jdf build -o`,编译工程并自动打开浏览器,假设打开的网址为: 92 | `http://192.168.191.1:8080`, 93 | 可以通过点击页面的文件路径一直跳转到: 94 | `http://192.168.191.1:8080/html/myPage.html` 95 | 96 | * myPage.html页面显示效果 97 | 98 |  99 | 100 | * 随意改动html,vm,js,scss文件,保存,可以在浏览器中看到改动同步刷新了。 101 | 102 | 你的项目开发进度良好! 103 | 104 | 105 | #### 输出项目 106 | 项目开发完以后,需要将编译后的文件放到线上服务器或者CDN,因此需要输出项目内容。 107 | 108 | 执行`jdf output`,jdf默认会将项目输出到`build`目录中,如果在config.json配置了`projectPath`,那么就会输出到`build/projectPath`中,例如 109 | 110 | ``` 111 | projectPath: 'helloworld/1.0.0' 112 | ``` 113 | 114 | 那么输出的目录结构为: 115 | 116 | ``` 117 | build/ 118 | └── helloworld 119 | └── 1.0.0 120 | ├── html 121 | │ └── index.html 122 | │ └── myPage.html 123 | └── widget 124 | └── myWidget 125 | ├── component.json 126 | ├── myWidget.css // scss -> css 127 | ├── myWidget.js 128 | └── myWidget.json 129 | ``` 130 | 131 | 恭喜你的项目开发完毕,让我们启动服务器试试输出的项目能不能工作吧! 132 | 133 | #### 启动服务器 134 | 进入build/helloworld/1.0.0目录,运行`jdf server`命令,开启一个静态服务器来查看输出结果是否正确。 135 | 136 | ``` 137 | ~/jd/web/helloworld/build/helloworld/1.0.0$ jdf server -o 138 | ``` 139 | 140 | 由于`jdf output`会根据你在config.json文件中的配置定制输出,通过`jdf server`来查看这些配置是否会影响页面效果是很有必要的。 141 | 142 | 检查完毕,页面和预期完全一致 143 | 144 | #### 上传到测试服务器测试 145 | 代码开发完毕,后端和业务方需要查看效果,这个时候就可以把代码上传到测试服务器,让大家都能访问。 146 | 147 | jdf上传的测试服务器可以是基于HTTP、FTP、SFTP协议的服务器,在config.json中配置好服务器地址: 148 | 149 | ``` 150 | host: xx.xx.xx.93 151 | ``` 152 | 153 | 然后执行: 154 | ``` 155 | jdf upload 156 | ``` 157 | 158 | 这样就上传到测试服务器了,邀请团队的小伙伴来查看你的成果吧。 159 | 160 | ## 结语 161 | 通过上述操作,你已经掌握jdf的主要功能,可以进行完整的工程开发了。jdf还有很多特性,我们也提供了完善的说明文档,欢迎探索。 162 | 163 | -------------------------------------------------------------------------------- /doc/a_tool_format.md: -------------------------------------------------------------------------------- 1 | # html/js/css文件格式化 2 | 3 | ## 使用说明 4 | 5 | * 方法1:在当前目录中,使用 `jdf format` 或者 `jdf format ./test` 可直接格式化当前目录或者指定目录下的所有文件。 6 | * 方法2:在当前目录中,使用 `jdf format test.html` 可直接格式化指定的文件。 7 | 8 | ## 使用示例 9 | 10 | `jdf format` 可快速格式化文件中的代码格式,比如 `test.html` 的内容如下: 11 | 12 | 13 | 14 | 15 | 16 |jdf format 42 |
43 |jdf server '+ 218 | ' IP '+serverIp+' '+ 219 | //''+new Date()+' '+ 220 | '
'; 221 | return copyright; 222 | } 223 | 224 | /** 225 | * 拼接展示目录的页面 226 | * @param {String} realPath 目录路径 227 | * @param {String} pathname 相对于根域名的路径 228 | * @return {HTML String} 拼接的html片段 229 | */ 230 | server.getDirList = function(realPath){ 231 | let data = { 232 | title: '', 233 | projectName: '', 234 | pathname: '', 235 | files: [], 236 | dirs: [] 237 | }; 238 | 239 | realPath = path.normalize(realPath); 240 | let serverDir = config.serverDir; 241 | let pathname = path.relative(serverDir, realPath); 242 | 243 | data.projectName = path.basename(serverDir); 244 | data.title = data.projectName + ' File List'; 245 | data.pathname = pathname; 246 | data.pathname = data.pathname.replace(new RegExp(`\\${path.sep}`, 'g'), '/'); 247 | 248 | // 返回上一级目录 249 | data.dirs.push({pathname: path.dirname(data.pathname), name: '..'}); 250 | 251 | let projAbsDir = realPath; 252 | // 不过滤任何内容 253 | fs.readdirSync(projAbsDir).forEach(name => { 254 | let absPath = path.join(projAbsDir, name); 255 | let pathname = path.relative(serverDir, absPath); 256 | 257 | // D:\\NodeApp\\jdfDev\\widget\\ -> (localhost:8080)/jdfDev/widget,用于浏览器地址栏,手动统一转为正斜杠 258 | pathname = pathname.replace(new RegExp(`\\${path.sep}`, 'g'), '/'); 259 | logger.verbose(`file or dir relative path: ${pathname}`); 260 | 261 | if (f.isDir(absPath)) { 262 | data.dirs.push({pathname: pathname, name: name}); 263 | } else if (f.isFile(absPath)) { 264 | data.files.push({pathname: pathname, name: name}); 265 | } 266 | }); 267 | 268 | let html = dirHTML.html(data); 269 | return html; 270 | } 271 | 272 | /** 273 | * 拼接combo文件 274 | * @param {Array} combolist 需要combo的路径列表 275 | * @return {String} combo后的文件内容 276 | */ 277 | server.getComboFilesContent = function (combolist) { 278 | let comboContent = ''; 279 | combolist.forEach(function(file){ 280 | let content = ''; 281 | if(f.exists(file)){ 282 | content = f.read(file); 283 | if (path.extname(file) === '.js') { 284 | // 解析js依赖,没有解析css依赖 285 | content = f.read(file); 286 | if(typeof(Compress.addJsDepends) === 'function'){ 287 | content = Compress.addJsDepends(file); 288 | } 289 | //如果代码的末尾没有分号,则自动添加一个。以避免代码合并出现异常。 290 | if(!/[;\r\n]$/.test(content)){ 291 | content += ';'; 292 | } 293 | } 294 | comboContent += content; 295 | }else{ 296 | comboContent = undefined; 297 | return false; 298 | } 299 | }); 300 | return comboContent;; 301 | } 302 | -------------------------------------------------------------------------------- /lib/server/middlewareVFS/envConfig.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | webRoot: '' 5 | } 6 | -------------------------------------------------------------------------------- /lib/server/middlewareVFS/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let mw = require('./middlewareVFS'); 4 | 5 | module.exports.init = mw.init; 6 | -------------------------------------------------------------------------------- /lib/server/middlewareVFS/middlewareVFS.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * 基于VFS系统的服务中间件 4 | * request会首先经过。。。。;。。。 5 | * @type {[type]} 6 | */ 7 | const url = require('url'); 8 | const fs = require('fs'); 9 | const path = require('path'); 10 | const f = require('jdf-utils').file; 11 | const mime = require('../mime'); 12 | const view = require('./view'); 13 | const VFS = require('../../VFS/VirtualFileSystem'); 14 | const logger = require('jdf-log'); 15 | 16 | let server = module.exports = {}; 17 | 18 | // enum 19 | let pathType = { 20 | combo: 1, 21 | dir: 2, 22 | file: 3, 23 | empty: 4 24 | }; 25 | 26 | server.init = function (projectDir) { 27 | view.config.webRoot = path.normalize(projectDir); 28 | 29 | return function (request, response, next) { 30 | let jdfRes = {}; 31 | let requestUrl = decodeURI(request.url); 32 | 33 | let resource = server.getResourceInfo(requestUrl); 34 | logger.verbose(`resource info: ${JSON.stringify(resource)}`); 35 | 36 | switch (resource.type) { 37 | case pathType.empty: jdfRes = server.Res404(); break; 38 | case pathType.combo: jdfRes = server.ResCombo(resource.filelist); break; 39 | case pathType.dir: jdfRes = server.ResDir(resource.filelist[0]); break; 40 | case pathType.file: jdfRes = server.ResFile(resource.filelist[0]); break; 41 | default: jdfRes = server.Res404(); 42 | } 43 | 44 | response.writeHead(jdfRes.status, { 45 | 'Content-Type': jdfRes.contentType 46 | }); 47 | response.write(jdfRes.content); 48 | response.end(); 49 | next(); 50 | } 51 | } 52 | 53 | /** 54 | * 根据请求url获取本地文件,目录路径 55 | * @param {String} requestUrl 请求url 56 | * @return {Object} 包含路径信息以及有关这个路径信息的元信息 57 | */ 58 | server.getResourceInfo = function (requestUrl) { 59 | requestUrl = requestUrl || ''; 60 | // example /a/b/c/ 61 | let parsedUrl = url.parse(requestUrl); 62 | let pathname = parsedUrl.pathname; 63 | let isComboUrl = /^\?\?/.test(parsedUrl.search); 64 | let fileNameList = []; 65 | let resource = {}; 66 | 67 | if (isComboUrl) { 68 | // ['a.js', 'b.js'] 69 | fileNameList = parsedUrl.query.slice(1).split(','); 70 | 71 | fileNameList = fileNameList.map(function (item) { 72 | // ['C://Users/xxx/AppData/Local/Temp/a.js'] 73 | return path.join(view.config.webRoot, pathname, decodeURI(item)); 74 | }); 75 | } else { 76 | // ['/a/b/c/'] 77 | fileNameList = [pathname]; 78 | fileNameList = fileNameList.map(function (item) { 79 | // ['C://Users/xxx/AppData/Local/Temp/a.js'] 80 | return path.join(view.config.webRoot, decodeURI(item)); 81 | }); 82 | } 83 | 84 | // type: combo 85 | if (isComboUrl) { 86 | 87 | resource.type = pathType.combo; 88 | } else if (f.isDir(fileNameList[0])) { 89 | // type: 目录 90 | resource.type = pathType.dir; 91 | } else { 92 | // defaults: file 93 | resource.type = pathType.file; 94 | } 95 | 96 | resource.filelist = fileNameList; 97 | return resource; 98 | } 99 | 100 | /** 101 | * 未找到资源返回404 102 | * @return {Object} 包含状态吗,返回内容类型,返回内容的对象 103 | */ 104 | function response404() { 105 | let notfound = view.res404(); 106 | return { 107 | status: 404, 108 | contentType: mime['html'], 109 | content: notfound 110 | }; 111 | } 112 | 113 | /** 114 | * 404处理函数 115 | * @type {[void]} 116 | */ 117 | server.Res404 = response404; 118 | 119 | /** 120 | * 文件资源处理函数 121 | * @param {String} resPath 文件资源路径 122 | * @return {Object} 包含状态吗,返回内容类型,返回内容的对象 123 | */ 124 | server.ResFile = function (resPath) { 125 | let ext = path.extname(resPath); 126 | ext = ext.replace('.', ''); 127 | let contentType = mime[ext] || mime['txt']; 128 | 129 | let content = view.fileRes.getFileContent(resPath); 130 | 131 | // 定义null为not found 132 | if (content === null) { 133 | return this.Res404(); 134 | } 135 | 136 | return { 137 | status: 200, 138 | contentType: contentType, 139 | content: content 140 | }; 141 | } 142 | 143 | /** 144 | * 目录访问处理函数 145 | * @param {String} resPath 目录路径 146 | * @param {String} urlPath url相对根域名的路径 147 | */ 148 | server.ResDir = function (resPath) { 149 | let html = view.dirView.genHTML(resPath); 150 | return { 151 | status: 200, 152 | contentType: mime['html'], 153 | content: html 154 | }; 155 | } 156 | 157 | /** 158 | * combo组装处理函数 159 | * @param {Array} resList 需要combo的文件列表 160 | * 备注2016-11-23:有require时解析依赖没有测试,这个测试应该放在compress.js中,正常文件combo是可以成功的 161 | */ 162 | server.ResCombo = function (resList) { 163 | let ext, type, content, res; 164 | 165 | if (resList && resList.length > 0) { 166 | ext = path.extname(resList[0]).slice(1); 167 | } 168 | type = mime[ext] || mime['txt']; 169 | 170 | content = view.comboRes.getComboContent(resList); 171 | 172 | if (content === undefined) { 173 | res = response404(); 174 | } else { 175 | res = { 176 | status: 200, 177 | contentType: type, 178 | content: content 179 | } 180 | } 181 | return res; 182 | } 183 | -------------------------------------------------------------------------------- /lib/server/middlewareVFS/res.404.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const res404 = module.exports = {}; 4 | 5 | res404.res404 = function () { 6 | return ` 7 | 8 | 9 | 10 | 11 | 12 | 13 |work well!`; 7 | return tpl; 8 | } 9 | -------------------------------------------------------------------------------- /lib/server/middlewareVFS/view.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const envConfig = require('./envConfig'); 4 | const dirView = require('./res.dirView'); 5 | const comboRes = require('./res.comboContent.js'); 6 | const notfound = require('./res.404.js'); 7 | const fileRes = require('./res.file.js'); 8 | 9 | const view = module.exports = {}; 10 | view.config = envConfig; 11 | 12 | view.dirView = dirView; 13 | view.comboRes = comboRes; 14 | view.res404 = notfound.res404; 15 | view.fileRes = fileRes; 16 | -------------------------------------------------------------------------------- /lib/server/mime.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "css": "text/css", 3 | "gif": "image/gif", 4 | "html": "text/html", 5 | "tpl": "text/html", 6 | "vm": "text/html", 7 | "shtml": "text/html", 8 | "ico": "image/x-icon", 9 | "jpeg": "image/jpeg", 10 | "jpg": "image/jpeg", 11 | "js": "application/javascript", 12 | "json": "application/json", 13 | "pdf": "application/pdf", 14 | "png": "image/png", 15 | "svg": "image/svg+xml", 16 | "swf": "application/x-shockwave-flash", 17 | "tiff": "image/tiff", 18 | "txt": "text/plain", 19 | "wav": "audio/x-wav", 20 | "wma": "audio/x-ms-wma", 21 | "wmv": "video/x-ms-wmv", 22 | "xml": "text/xml", 23 | "ttf":"font/ttf", 24 | "otf":"font/opentype", 25 | "woff":"application/font-woff", 26 | "woff2":"application/font-woff2", 27 | "eot":"application/vnd.ms-fontobject" 28 | }; -------------------------------------------------------------------------------- /lib/server/openurl.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @openurl 5 | * @from https://github.com/rauschma/openurl/blob/master/openurl.js 6 | * @example require("./openurl").open(url); 7 | */ 8 | const spawn = require('child_process').spawn; 9 | let command; 10 | 11 | switch(process.platform) { 12 | case 'darwin': 13 | command = 'open'; 14 | break; 15 | case 'win32': 16 | command = 'explorer.exe'; 17 | break; 18 | case 'linux': 19 | command = 'xdg-open'; 20 | break; 21 | default: 22 | throw new Error('Unsupported platform: ' + process.platform); 23 | } 24 | 25 | /** 26 | * Error handling is deliberately minimal, as this function is to be easy to use for shell scripting 27 | * 28 | * @param url The URL to open 29 | * @param callback A function with a single error argument. Optional. 30 | */ 31 | 32 | function open(url, callback) { 33 | const child = spawn(command, [url]); 34 | let errorText = ''; 35 | child.stderr.setEncoding('utf8'); 36 | child.stderr.on('data', function (data) { 37 | errorText += data; 38 | }); 39 | child.stderr.on('end', function () { 40 | if (errorText.length > 0) { 41 | let error = new Error(errorText); 42 | if (callback) { 43 | callback(error); 44 | } else { 45 | throw error; 46 | } 47 | } else if (callback) { 48 | callback(error); 49 | } 50 | }); 51 | } 52 | 53 | module.exports.open = open; 54 | -------------------------------------------------------------------------------- /lib/vm.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | const util = require('util'); 6 | const jdfUtils = require('jdf-utils'); 7 | const logger = require('jdf-log'); 8 | const $ = jdfUtils.base; 9 | const f = jdfUtils.file; 10 | const jdf = require("./jdf"); 11 | const velocity = require('velocityjs'); 12 | const VFS = require('./VFS/VirtualFileSystem'); 13 | const vm = module.exports; 14 | 15 | /** 16 | * @rander data 17 | * 格式: 18 | * 19 | * 20 | * 21 | * vm's content 22 | * 23 | * @{String} vmSource vm内容 24 | * @{Object} dataObj vm对应的数据 25 | * @{String} dirname vm的dirname 26 | */ 27 | vm.render = function(vmSource, options) { 28 | let dataObj = options.dataObj; 29 | let dirname = options.dirname; 30 | let existMap = options.existMap; 31 | 32 | if (!(vmSource && dataObj)) { 33 | return ''; 34 | } 35 | 36 | let macros = { 37 | parse: function (name) { 38 | let content = `\n`; 39 | let vmpath = path.resolve(dirname, name); 40 | let vmVfile = VFS.queryFile(vmpath); 41 | if (!vmVfile) { 42 | logger.error(`'#parse(${name})' in ${dirname}'s vm, path not exist or not a file`); 43 | return name; 44 | } 45 | 46 | // TODO content不将js css放进去,只放到cssMap,jsMap中 47 | 48 | // 添加#parse(vmpath)相应的css和js标签 49 | let vmName = path.basename(vmpath).replace(path.extname(vmpath), ''); 50 | if (!existMap.cssMap[vmName]) { 51 | // 获取vfile,没有就不添加css标签到html中 52 | let csspath = vmpath.replace(path.extname(vmpath), '.css'); 53 | csspath = path.relative(VFS.originDir, csspath); 54 | csspath = path.join(VFS.targetDir, csspath); 55 | let cssVfile = VFS.queryFile(csspath, 'target'); 56 | if (cssVfile) { 57 | existMap.cssMap.set(vmName, true); 58 | } 59 | } 60 | if (!existMap.jsMap[vmName]) { 61 | let jspath = vmpath.replace(path.extname(vmpath), '.js'); 62 | jspath = path.relative(VFS.originDir, jspath); 63 | jspath = path.join(VFS.targetDir, jspath); 64 | let jsVfile = VFS.queryFile(jspath, 'target'); 65 | if (jsVfile) { 66 | existMap.jsMap.set(vmName, true); 67 | } 68 | } 69 | // 递归解析 70 | content += vm.render(vmVfile.targetContent, { 71 | dataObj: dataObj, 72 | dirname: path.dirname(vmpath), 73 | existMap: existMap 74 | }); 75 | 76 | content += ``; 77 | 78 | return content; 79 | } 80 | } 81 | 82 | return velocity.render(vmSource, dataObj, macros); 83 | } 84 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jdfx", 3 | "description": "京东前端自动化构建工具", 4 | "version": "3.4.13", 5 | "author": { 6 | "name": "jdfx", 7 | "email": "chenxiaochun@jd.com" 8 | }, 9 | "homepage": "https://github.com/jdf2e/jdf", 10 | "keywords": [ 11 | "jdf", 12 | "jdfx", 13 | "build tools", 14 | "构建工具", 15 | "tool", 16 | "cli", 17 | "front-end", 18 | "webpack", 19 | "fe" 20 | ], 21 | "license": "MIT", 22 | "bin": { 23 | "jdf": "bin/jdf" 24 | }, 25 | "engines": { 26 | "node": ">= 4.4.5" 27 | }, 28 | "main": "index.js", 29 | "scripts": { 30 | "test": "mocha test/index.js" 31 | }, 32 | "repository": { 33 | "type": "git", 34 | "url": "https://github.com/jdf2e/jdf.git" 35 | }, 36 | "bugs": { 37 | "url": "https://github.com/jdf2e/jdf/issues" 38 | }, 39 | "preferGlobal": true, 40 | "readmeFilename": "README.md", 41 | "dependencies": { 42 | "acorn": "^4.0.4", 43 | "atropa-jslint": "0.1.2", 44 | "autoprefixer": "^6.5.2", 45 | "axios": "^0.17.0", 46 | "babel-core": "^6.18.0", 47 | "babel-preset-env": "^1.7.0", 48 | "browser-sync": "2.21.0", 49 | "bs-html-injector": "^3.0.3", 50 | "cheerio": "^0.22.0", 51 | "clean-css": "2.1.8", 52 | "commander": "^2.9.0", 53 | "csslint": "0.10.0", 54 | "escape-string-regexp": "^1.0.5", 55 | "escodegen": "^1.8.1", 56 | "fs-extra": "^1.0.0", 57 | "glob": "^7.1.1", 58 | "html-minifier": "0.6.9", 59 | "htmllint": "0.0.7", 60 | "iconv-lite": "^0.4.10", 61 | "jdf-css-sprite": "^1.1.6", 62 | "jdf-img-minify": "0.1.0", 63 | "jdf-log": "^0.0.4", 64 | "jdf-upload": "^0.2.0", 65 | "jdf-utils": "^1.1.3", 66 | "js-beautify": "1.5.4", 67 | "jsmart": "^2.14.0", 68 | "less": "^2.7.1", 69 | "lodash": "^4.17.2", 70 | "minimatch": "^3.0.3", 71 | "node-sass": "^4.14.1", 72 | "node-watch": "^0.5.3", 73 | "postcss": "^5.2.5", 74 | "shelljs": "^0.7.5", 75 | "socket.io": "^2.0.4", 76 | "socket.io-client": "^2.0.4", 77 | "strip-json-comments": "^2.0.1", 78 | "uglify-es": "^3.1.3", 79 | "uglify-js": "2.6.0", 80 | "uuid": "^3.0.0", 81 | "velocityjs": "0.4.3" 82 | }, 83 | "devDependencies": { 84 | "expect.js": "^0.3.1", 85 | "mocha": "^3.2.0" 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /template/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "host": "192.168.181.73", 3 | "user": "arch", 4 | "password": "arch", 5 | "projectPath": "jdf_init", // 默认输出项目名称 6 | "compressThreadCrisis": 200, // 工程文件超过200个时开启多线程压缩 7 | "cdn": "//misc.360buyimg.com", // cdn前缀,如果不需要cdn,置成空字符串 8 | "output": { 9 | "cssCombo": true, // css进行combo 10 | "jsCombo": true, // js进行combo 11 | 12 | "compressJs": true, // 是否开启压缩js文件 13 | "compressCss": true, // 是否开启压缩css文件 14 | "compressImage": true, // 是否开启压缩图片 15 | 16 | "excludeFiles": "" // 不需要输出的文件/文件夹,路径相对于当前项目根目录,以逗号分隔,例如:"test,test.css" 17 | }, 18 | "plugins": [] // 引用的插件 19 | } -------------------------------------------------------------------------------- /template/css/style.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdf2e/jdf/8ef48da64ca10305ee47e8c0aff6e23a3d647c37/template/css/style.scss -------------------------------------------------------------------------------- /template/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 |2016-12-12
55 |jdf命令行工具是jdf前端集成解决方案的工具集,基于node.js和npm,jdf前需要先安装node.js,然后通过npm命令安装jdfx
58 | 59 |npm install jdfx -g
60 |
61 | 安装测试,执行如下命令,如果出现版本号则说明你已安装成功
62 | 63 |jdf -V
64 |
65 | 更新jdf
66 | 67 |npm update jdfx -g
68 |
69 | 在本机svn目录,主干上新建项目,比如product下项目test,即svn目录为/product/test
注:product为大项目名,test为测试项目
从命令行下进入test文件夹,在命令行执行如下命令,生成标准化的项目文件夹
76 | 77 |jdf init [projectName]
78 |
79 | test文件中里包括css、html、js、widget、config.json,其中config.json为配置文件,所有配置都需要修改此文件
80 | 81 |注1:json文件不允许有注释,单双引号使用时也需要统一
注2:windows7下在当前文件夹打开命令行的方法:Shift+鼠标右键,选择"在此处打开命令窗口"
注3:windows下CMD路径常用操作如下
在html,js,css等文件夹中新建相应文件
在widget文件夹新建抽离规划好的widget模块
在当前项目中,新建widget的命令为jdf widget -create xxx
可以通过jdf build
在浏览器中查看构建后的当前工程,包括less,sass编译,widget编译等
jdf build
92 |
93 | 注意
94 | 95 |ctrl+c
命令退出本地serverjdf clean
,清除服务器后台缓存,强制同步一次本地调试服务器启动成功后,可能通过jdf build -o
命令自动打开浏览器,也可以复制http://localhost/
在浏览上打开,jdf内置了browserSync,会自动同步刷新页面
jdf output
输出项目文件夹,包括压缩合并后的css,js,images,静态资源加cdn前缀,同时压缩所有图片jdf output js/test.js css
自定义输出自己需要的文件或文件夹
jdf upload
发布至远端机器,主要包括css/js/widget,供产品,设计师查看效果,以及后端工程师联调
110 |
111 |
113 | jdfx官方地址 114 |
115 |aaabwww.jd.com
46 | {%widgetOutputName="name1"%} 47 | 48 |