diff --git a/.eslintignore b/.eslintignore index bdf4165..0469a4a 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,4 @@ dist/ temp/ -*.min.js \ No newline at end of file +*.min.js +node_modules \ No newline at end of file diff --git a/.gitignore b/.gitignore index 4db6739..1e5f855 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,8 @@ dist temp !test *.tgz +*.cache.* packages/app/main.js -packages/app/index.html \ No newline at end of file +packages/app/index.html +packages/monaco/index.css +packages/dev-server/themes/*.json \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 691d6e0..6a2d8de 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,6 +5,7 @@ "vue" ], "files.exclude": { + "**/*.cache.*": true, "**/node_modules": true, "**/package-lock.json": true, "package.json.lerna_backup": true, diff --git a/README.md b/README.md index 460db34..a61ceda 100644 --- a/README.md +++ b/README.md @@ -9,33 +9,33 @@ A markup language designed for API manual pages. ## Packages -- [markletjs](https://github.com/obstudio/Marklet/tree/master/packages/marklet): [![npm](https://img.shields.io/npm/v/markletjs.svg)](https://www.npmjs.com/package/markletjs) -- [@marklet/cli](https://github.com/obstudio/Marklet/tree/master/packages/cli): [![npm](https://img.shields.io/npm/v/@marklet/cli.svg)](https://www.npmjs.com/package/@marklet/cli) -- [@marklet/core](https://github.com/obstudio/Marklet/tree/master/packages/core): [![npm](https://img.shields.io/npm/v/@marklet/core.svg)](https://www.npmjs.com/package/@marklet/core) -- [@marklet/dev-server](https://github.com/obstudio/Marklet/tree/master/packages/dev-server): [![npm](https://img.shields.io/npm/v/@marklet/dev-server.svg)](https://www.npmjs.com/package/@marklet/dev-server) -- [@marklet/monaco](https://github.com/obstudio/Marklet/tree/master/packages/monaco): [![npm](https://img.shields.io/npm/v/@marklet/monaco.svg)](https://www.npmjs.com/package/@marklet/monaco) -- [@marklet/parser](https://github.com/obstudio/Marklet/tree/master/packages/parser): [![npm](https://img.shields.io/npm/v/@marklet/parser.svg)](https://www.npmjs.com/package/@marklet/parser) -- [@marklet/renderer](https://github.com/obstudio/Marklet/tree/master/packages/renderer): [![npm](https://img.shields.io/npm/v/@marklet/renderer.svg)](https://www.npmjs.com/package/@marklet/renderer) +| package | version | +|:-------:|:-------:| +|[@marklet/cli](https://github.com/obstudio/Marklet/tree/master/packages/cli)|[![npm](https://img.shields.io/npm/v/@marklet/cli.svg)](https://www.npmjs.com/package/@marklet/cli)| +|[@marklet/core](https://github.com/obstudio/Marklet/tree/master/packages/core)|[![npm](https://img.shields.io/npm/v/@marklet/core.svg)](https://www.npmjs.com/package/@marklet/core)| +|[@marklet/dev-server](https://github.com/obstudio/Marklet/tree/master/packages/dev-server)|[![npm](https://img.shields.io/npm/v/@marklet/dev-server.svg)](https://www.npmjs.com/package/@marklet/dev-server)| +|[@marklet/parser](https://github.com/obstudio/Marklet/tree/master/packages/parser)|[![npm](https://img.shields.io/npm/v/@marklet/parser.svg)](https://www.npmjs.com/package/@marklet/parser)| +|[@marklet/renderer](https://github.com/obstudio/Marklet/tree/master/packages/renderer)|[![npm](https://img.shields.io/npm/v/@marklet/renderer.svg)](https://www.npmjs.com/package/@marklet/renderer)| ## Usage: CLI ``` -Usage: marklet [filepath|dirpath] [options] +Usage: marklet [options] [command] + +A command line interface for marklet. Options: + -v, --version output the version number + -h, --help output usage information - -v, --version output the version number - -m, --mode [mode] Choose between parse, watch and edit mode (default: parse) - -s, --source [path] Read text from file - -i, --input [text] Read text directly from stdin - -d, --dest [path] Write parsed data to file instead of stdin - -p, --port [port] Port for the development server - -l, --default-language [language] Default language in code block - -H, --no-header-align Disable header to align at center - -S, --no-section Disallow section syntax - -h, --help output usage information +Commands: + parse [options] [filepath] Parse a marklet file into marklet AST. + edit [options] [filepath|dirpath] Edit a marklet file or project. + watch [options] [filepath|dirpath] Watch a marklet file or project. ``` +See details [here](https://github.com/obstudio/Marklet/blob/master/packages/cli/README.md). + ## Usage: Node ```shell diff --git a/build/build.js b/build/build.js index 943fe09..682d6a5 100644 --- a/build/build.js +++ b/build/build.js @@ -1,11 +1,14 @@ -const util = require('./util') +const themes = require('../packages/renderer/themes') const { minify } = require('html-minifier') const program = require('commander') const webpack = require('webpack') const sfc2js = require('sfc2js') +const sass = require('node-sass') +const yaml = require('js-yaml') +const util = require('./util') const fs = require('fs') -sfc2js.install(require('@sfc2js/sass')) +sfc2js.install(require('@sfc2js/node-sass')) sfc2js.install(require('@sfc2js/clean-css')) function mkdirIfNotExists(name) { @@ -64,58 +67,119 @@ const bundle = (name, options) => new Promise((resolve, reject) => { }) Promise.resolve().then(() => { - if (program.renderer) { - mkdirIfNotExists('renderer/dist') - - sfc2js.transpile({ - ...sfc2jsOptions, - baseDir: util.resolve('renderer'), - outCSSFile: '../dist/marklet.min.css', - defaultScript: { - props: ['node'], - }, - }) + if (!program.renderer) return + mkdirIfNotExists('renderer/dist') + let css = '' + return sfc2js.transpile({ + ...sfc2jsOptions, + baseDir: util.resolve('renderer'), + outCSSFile: '../dist/marklet.min.css', + defaultScript: { + props: ['node'], + }, + }).then((result) => { + if (result.errors.length) throw result.errors.join('\n') + return Promise.all(themes.map(({ key }) => new Promise((resolve, reject) => { + const filepath = util.resolve('renderer/themes', key + '.scss') + sass.render({ + data: `.${key}.marklet{${fs.readFileSync(filepath).toString()}}`, + outputStyle: 'compressed', + }, (error, result) => { + if (error) { + reject(error) + } else { + css += result.css.toString() + resolve() + } + }) + }))) + }).then(() => { + fs.writeFileSync(util.resolve('renderer/dist/themes.min.css'), css) return bundle('renderer', { entry: 'src/index.js', output: 'renderer.min.js', }) - } + }) }).then(() => { - if (program.server) { - mkdirIfNotExists('dev-server/dist') - - if (program.tsc) { - util.exec('tsc -p packages/dev-server') - } + if (!program.server) return + mkdirIfNotExists('dev-server/dist') + + if (program.tsc) { + util.exec('tsc -p packages/dev-server') + } + function minifyHTML(type) { + const srcPath = util.resolve(`dev-server/src/${type}.html`) + const distPath = util.resolve(`dev-server/dist/${type}.html`) if (program.prod) { fs.writeFileSync( - util.resolve('dev-server/dist/index.html'), - minify(fs.readFileSync(util.resolve('dev-server/src/index.html')).toString(), { + distPath, + minify(fs.readFileSync(srcPath).toString(), { collapseWhitespace: true, removeAttributeQuotes: true, }) ) } else { - fs.copyFileSync( - util.resolve('dev-server/src/index.html'), - util.resolve('dev-server/dist/index.html') - ) + fs.copyFileSync(srcPath, distPath) } + } + + minifyHTML('edit') + minifyHTML('watch') + + let css = '' + themes.forEach(({ key }) => { + const options = yaml.safeLoad(fs.readFileSync(util.resolve(`dev-server/themes/${key}.yaml`))) + fs.writeFileSync(util.resolve(`dev-server/themes/${key}.json`), JSON.stringify(options)) + }) - sfc2js.transpile({ - ...sfc2jsOptions, - baseDir: util.resolve('dev-server'), - outCSSFile: '../dist/client.min.css', - }) + return sfc2js.transpile({ + ...sfc2jsOptions, + useCache: false, + baseDir: util.resolve('dev-server'), + outCSSFile: '../dist/client.min.css', + }).then((result) => { + if (result.errors.length) throw result.errors.join('\n') + return Promise.all(themes.map(({ key }) => new Promise((resolve, reject) => { + const filepath = util.resolve('dev-server/themes/' + key) + + try { + const options = yaml.safeLoad(fs.readFileSync(filepath + '.yaml')) + fs.writeFileSync(filepath + '.json', JSON.stringify(options)) + } catch (error) { + reject(error) + } - return bundle('dev-server', { - entry: 'dist/client.js', - output: 'client.min.js', - libraryExport: 'Marklet', + sass.render({ + data: `.${key}.marklet{${fs.readFileSync(filepath + '.scss')}}`, + outputStyle: 'compressed', + }, (error, result) => { + if (error) { + reject(error) + } else { + css += result.css + resolve() + } + }) + }))) + }).then(() => { + fs.writeFileSync(util.resolve('dev-server/dist/themes.min.css'), css) + return new Promise((resolve, reject) => { + sass.render({ + data: fs.readFileSync(util.resolve('dev-server/src/monaco.scss')).toString(), + outputStyle: 'compressed', + }, (error, result) => { + if (error) reject(error) + fs.writeFileSync(util.resolve('dev-server/dist/monaco.min.css'), result.css) + resolve() + }) }) - } + }).then(() => bundle('dev-server', { + entry: 'dist/client.js', + output: 'client.min.js', + libraryExport: 'Marklet', + })) }).catch((error) => { console.log(error) }) diff --git a/package.json b/package.json index 8578516..8050427 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "scripts": { - "start": "npm run build && node packages/cli -m edit", - "bootstrap": "lerna bootstrap --hoist --no-ci", + "start": "node build/build -r && node packages/cli edit", + "bootstrap": "lerna bootstrap --no-ci", "build": "tsc -b && node build/build -sr", "build:prod": "tsc -b && node build/build -psr", "build:renderer": "node build/build -r", @@ -11,29 +11,30 @@ "lint": "tslint packages/**/src/*.ts && eslint ." }, "devDependencies": { - "@octokit/rest": "^15.13.0", + "@octokit/rest": "^15.15.1", "@sfc2js/clean-css": "^1.1.1", - "@sfc2js/sass": "^1.0.1", + "@sfc2js/node-sass": "^2.0.0", + "@sfc2js/sass": "^2.0.0", "@types/cheerio": "^0.22.9", "@types/js-yaml": "^3.11.2", - "@types/node": "^10.11.4", + "@types/node": "^10.12.0", "@types/ws": "^6.0.1", "ajv": "^6.5.4", "chalk": "^2.4.1", "cheerio": "^1.0.0-rc.2", - "commander": "^2.18.0", - "electron": "^3.0.2", - "eslint": "^5.6.1", + "commander": "^2.19.0", + "electron": "^3.0.5", + "eslint": "^5.7.0", "eslint-plugin-vue": "^5.0.0-beta.3", "fast-deep-equal": "^2.0.1", "html-minifier": "^3.5.20", - "lerna": "^3.4.1", - "node-sass": "^4.9.3", - "sass": "^1.14.1", - "semver": "^5.5.1", - "sfc2js": "^3.3.2", + "lerna": "^3.4.3", + "node-sass": "^4.9.4", + "sass": "^1.14.3", + "semver": "^5.6.0", + "sfc2js": "^4.0.0-beta.1", "tslint": "^5.11.0", - "typescript": "^3.1.1", - "webpack": "^4.20.2" + "typescript": "^3.1.3", + "webpack": "^4.22.0" } } diff --git a/packages/app/build/scss.js b/packages/app/build/scss.js new file mode 100644 index 0000000..69cca55 --- /dev/null +++ b/packages/app/build/scss.js @@ -0,0 +1,38 @@ +const util = require('../../../build/util') +const sass = require('sass') +const fs = require('fs') + +function load({ + src, + dest = src, + selector, + base = '', + cache = dest, + style = 'compressed', +}) { + let cacheData = '' + try { + cacheData = fs.readFileSync(util.resolve(base, cache + '.cache.scss')).toString() + } catch (error) { /**/ } + + const source = fs.readFileSync(util.resolve(base, src + '.scss')).toString() + const srcData = selector ? `${selector}{${source}}` : source + + if (srcData === cacheData && fs.existsSync(util.resolve(dest + '.css'))) { + return fs.readFileSync(util.resolve(base, dest + '.css')).toString() + } else { + const destData = sass.renderSync({ data: srcData, outputStyle: style }).css.toString() + fs.writeFileSync(util.resolve(base, dest + '.css'), destData) + fs.writeFileSync(util.resolve(base, cache + '.cache.scss'), srcData) + return destData + } +} + +function loadAll(...options) { + return options.map(load).join('') +} + +module.exports = { + load, + loadAll, +} diff --git a/packages/app/build/sfc2js.js b/packages/app/build/sfc2js.js new file mode 100644 index 0000000..e04bee5 --- /dev/null +++ b/packages/app/build/sfc2js.js @@ -0,0 +1,26 @@ +const sfc2js = require('sfc2js') +const sass = require('sass') +const path = require('path') + +sfc2js.install({ + name: 'sass-plugin', + version: '1.0', + target: 'style', + lang: [ + 'sass', + 'scss', + 'css', + ], + default: { + includePaths: [], + }, + updated(options) { + const dirPath = path.dirname(options.srcPath) + this.options.includePaths.push(dirPath) + }, + render(style) { + return sass.renderSync({ ...this.options, data: style.content }).css.toString() + }, +}) + +module.exports = sfc2js diff --git a/packages/app/build/transpile.js b/packages/app/build/transpile.js index 80d914e..bf03b7f 100644 --- a/packages/app/build/transpile.js +++ b/packages/app/build/transpile.js @@ -1,32 +1,11 @@ const util = require('../../../build/util') -const sfc2js = require('sfc2js') -const sass = require('sass') -const path = require('path') +const sfc2js = require('./sfc2js') +const scss = require('./scss') +const yaml = require('js-yaml') const fs = require('fs') util.start() -sfc2js.install({ - name: 'sass-plugin', - version: '1.0', - target: 'style', - lang: [ - 'sass', - 'scss', - 'css', - ], - default: { - includePaths: [], - }, - updated(options) { - const dirPath = path.dirname(options.srcPath) - this.options.includePaths.push(dirPath) - }, - render(style) { - return sass.renderSync({ ...this.options, data: style.content }).css.toString() - }, -}) - module.exports = sfc2js.transpile({ baseDir: util.resolve(), srcDir: 'app/comp', @@ -34,21 +13,20 @@ module.exports = sfc2js.transpile({ enterance: util.isElectron() ? 'app.vue' : '', }) -let indexCache = '' -try { - indexCache = fs.readFileSync(util.resolve('app/temp/index.cache.scss')).toString() -} catch (error) { /**/ } - -const indexData = fs.readFileSync(util.resolve('app/comp/index.scss')).toString() - -if (indexData === indexCache && fs.existsSync(util.resolve('app/temp/index.css'))) { - module.exports.css += fs.readFileSync(util.resolve('app/temp/index.css')).toString() -} else { - const indexCSS = sass.renderSync({ data: indexData }).css.toString() - fs.writeFileSync(util.resolve('app/temp/index.css'), indexCSS) - fs.writeFileSync(util.resolve('app/temp/index.cache.scss'), indexCache) - module.exports.css += indexCSS -} +module.exports.css += scss.loadAll({ + base: 'renderer', + src: 'themes/simple/index', + dest: 'dist/simple', + selector: '.simple', +}, { + base: 'renderer', + src: 'themes/dark/index', + dest: 'dist/dark', + selector: '.dark', +}, { + base: 'monaco', + src: 'index', +}) if (util.isElectron()) { const result = sfc2js.transpile({ @@ -61,6 +39,12 @@ if (util.isElectron()) { props: ['node'], }, }) + + module.exports.themes = { + dark: yaml.safeLoad(fs.readFileSync(util.resolve('renderer/themes/dark/monaco.yaml'))), + simple: yaml.safeLoad(fs.readFileSync(util.resolve('renderer/themes/simple/monaco.yaml'))), + } + module.exports.css += result.css module.exports.plugin = result.app } diff --git a/packages/app/comp/app.vue b/packages/app/comp/app.vue index 684e3cd..830b1fb 100644 --- a/packages/app/comp/app.vue +++ b/packages/app/comp/app.vue @@ -5,10 +5,14 @@ const monacoLoader = require('@marklet/monaco') Vue.use(monacoLoader) +const themes = ['simple', 'dark'] + module.exports = { el: '#app', data: () => ({ + themeInput: '', + theme: 'dark', origin: '', source: '', nodes: [], @@ -25,7 +29,15 @@ module.exports = { this._editor.setModel(this._model) this.layout() } - } + }, + theme(value) { + if (window.monaco) { + window.monaco.editor.setTheme(value) + } + }, + themeInput(value) { + if (themes.includes(value)) this.theme = value + }, }, created() { @@ -35,6 +47,9 @@ module.exports = { this._lexer = new DocumentLexer() monacoLoader.then((monaco) => { + themes.forEach((name) => { + monaco.editor.defineTheme(name, window.result.themes[name]) + }) const model = monaco.editor.createModel(this.source, 'marklet') model.onDidChangeContent(() => this.checkChange()) const nodes = this.nodes @@ -49,7 +64,7 @@ module.exports = { window.vm = this monacoLoader.then((monaco) => { - monaco.editor.setTheme('vs') + monaco.editor.setTheme(this.theme) this._editor = monaco.editor.create(this.$refs.input, { model: null, language: 'marklet', @@ -102,9 +117,12 @@ module.exports = {