diff --git a/edit/codemirror-editing-hooks.js b/edit/codemirror-editing-hooks.js
index e06ae770..574e71ca 100644
--- a/edit/codemirror-editing-hooks.js
+++ b/edit/codemirror-editing-hooks.js
@@ -482,6 +482,7 @@ onDOMscriptReady('/codemirror.js').then(() => {
if (!chrome.runtime.getPackageDirectoryEntry) {
const themes = [
chrome.i18n.getMessage('defaultTheme'),
+ /* populate-theme-start*/
'3024-day',
'3024-night',
'abcdef',
@@ -531,7 +532,8 @@ onDOMscriptReady('/codemirror.js').then(() => {
'xq-dark',
'xq-light',
'yeti',
- 'zenburn',
+ 'zenburn'
+ /* populate-theme-end */
];
localStorage.codeMirrorThemes = themes.join(' ');
return Promise.resolve(themes);
diff --git a/package.json b/package.json
index 445ae382..434388f8 100644
--- a/package.json
+++ b/package.json
@@ -4,5 +4,17 @@
"description": "Redesign the web with Stylus, a user styles manager",
"license": "GPL-3.0-only",
"repository": "openstyles/stylus",
- "author": "Stylus Team"
+ "author": "Stylus Team",
+ "devDependencies": {
+ "codemirror": "^5.39.0",
+ "eslint": "^5.1.0",
+ "fs-extra": "^7.0.0",
+ "updates": "^3.2.3"
+ },
+ "scripts": {
+ "build": "npm run update && npm run build:cm",
+ "build:cm": "node tools/update-libraries.js && node tools/update-codemirror-themes.js",
+ "lint": "eslint **/*.js || true",
+ "update": "updates -u && npm update"
+ }
}
diff --git a/tools/update-codemirror-themes.js b/tools/update-codemirror-themes.js
new file mode 100644
index 00000000..ee3c37e9
--- /dev/null
+++ b/tools/update-codemirror-themes.js
@@ -0,0 +1,40 @@
+#!/usr/bin/env node
+'use strict';
+
+const fs = require('fs-extra');
+const path = require('path');
+
+// Update theme names list in codemirror-editing-hook.js
+async function getThemes() {
+ const p = path.join(__dirname, '..', 'vendor/codemirror/theme/');
+ const files = await fs.readdir(p);
+ return files
+ .filter(name => name.endsWith('.css'))
+ .map(name => name.replace('.css', ''))
+ .sort();
+}
+
+function replaceThemes(content, themes) {
+ const list = JSON.stringify(themes, null, 8).replace(/"/g, '\'');
+ return content.replace(
+ /\/\*\s*populate-theme-start\s*\*\/[\s\S]+\/\*\s*populate-theme-end\s*\*\//,
+ // strip off square brackets & first 8 spaces
+ `/* populate-theme-start*/\n${list.substring(2, list.length - 2)}\n /* populate-theme-end */`
+ );
+}
+
+async function updateHook(themes) {
+ const fileName = path.join(__dirname, '..', 'edit/codemirror-editing-hooks.js');
+ const content = await fs.readFile(fileName, 'utf-8');
+ fs.writeFile(fileName, replaceThemes(content, themes));
+}
+
+function exit(err) {
+ if (err) console.error(err);
+ process.exit(err ? 1 : 0);
+}
+
+getThemes()
+ .then(themes => updateHook(themes))
+ .then(() => console.log('\x1b[32m%s\x1b[0m', `codemirror themes list updated`))
+ .catch(exit);
diff --git a/tools/update-libraries.js b/tools/update-libraries.js
new file mode 100644
index 00000000..87e5a544
--- /dev/null
+++ b/tools/update-libraries.js
@@ -0,0 +1,69 @@
+#!/usr/bin/env node
+'use strict';
+
+const fs = require('fs-extra');
+const path = require('path');
+
+const root = path.join(__dirname, '..');
+
+const files = {
+ 'codemirror': [
+ 'addon/comment/comment.js',
+ 'addon/dialog',
+ 'addon/edit/closebrackets.js',
+ 'addon/edit/matchbrackets.js',
+ 'addon/fold/brace-fold.js',
+ 'addon/fold/comment-fold.js',
+ 'addon/fold/foldcode.js',
+ 'addon/fold/foldgutter.css',
+ 'addon/fold/foldgutter.js',
+ 'addon/fold/indent-fold.js',
+ 'addon/hint/css-hint.js',
+ 'addon/hint/show-hint.css',
+ 'addon/hint/show-hint.js',
+ 'addon/lint/css-lint.js',
+ 'addon/lint/json-lint.js',
+ 'addon/lint/lint.css',
+ 'addon/lint/lint.js',
+ 'addon/scroll/annotatescrollbar.js',
+ 'addon/search/match-highlighter.js',
+ 'addon/search/matchesonscrollbar.css',
+ 'addon/search/matchesonscrollbar.js',
+ 'addon/search/searchcursor.js',
+ 'addon/selection/active-line.js',
+ 'keymap',
+ 'lib',
+ 'mode/css',
+ 'mode/javascript',
+ 'mode/stylus',
+ 'theme'
+ ]
+};
+
+async function updateReadme(lib) {
+ const pkg = await fs.readJson(`${root}/node_modules/${lib}/package.json`);
+ const file = `${root}/vendor/${lib}/README.md`;
+ const txt = await fs.readFile(file, 'utf8');
+ return fs.writeFile(file, txt.replace(/##\s*v[-\w.]+/, `## v${pkg.version}`));
+}
+
+async function copy(lib, folder) {
+ try {
+ await fs.copy(`${root}/node_modules/${lib}/${folder}`, `${root}/vendor/${lib}/${folder}`);
+ } catch (err) {
+ exit(err);
+ }
+}
+
+function exit(err) {
+ if (err) console.error(err);
+ process.exit(err ? 1 : 0);
+}
+
+Object.keys(files).forEach(lib => {
+ updateReadme(lib);
+ files[lib].forEach(folder => {
+ copy(lib, folder);
+ });
+ console.log('\x1b[32m%s\x1b[0m', `${lib} files updated`);
+});
diff --git a/vendor/README b/vendor/README
new file mode 100644
index 00000000..a0517e71
--- /dev/null
+++ b/vendor/README
@@ -0,0 +1,21 @@
+# Vendor files are populated by the build script:
+
+## What the build script does
+
+Using this repo, run `npm install`... the latest versions of:
+
+* `CodeMirror` (https://github.com/codemirror/CodeMirror) is installed.
+* **TODO**: `jsonlint` () is installed.
+* **TODO**: `less` () is installed.
+* **TODO**: `lz-string-unsafe` (https://github.com/openstyles/lz-string-unsafe) is installed.
+* **TODO**: `node-semver` () is installed.
+* **TODO**: `stylus-lang` () is installed.
+* The necessary build tools; see `devDependencies` in the `package.json`.
+
+## Running the build script
+
+Use `npm run build` to update packages in the `node_modules` folder & update the vendor folder.
+
+The following changes are made:
+
+* Only the essential CodeMirror files are copied directly from the `node_modules` folder to `vendor/codemirror`; see the `vendor/codemirror/README` for specifics.
diff --git a/vendor/codemirror/README.md b/vendor/codemirror/README.md
new file mode 100644
index 00000000..a325dbf6
--- /dev/null
+++ b/vendor/codemirror/README.md
@@ -0,0 +1,43 @@
+## v0.0.0
+
+List of essential folders & files copied from `node_modules/codemirror` to `vendor/codemirror`:
+
+_ addon/
+| |_ comment/
+| | |_ comment.js
+| |_ dialog/* (all files)
+| |_ edit/
+| | |_ closebrackets.js
+| | |_ matchbrackets.js
+| |_ fold/
+| | |_ brace-fold.js
+| | |_ comment-fold.js
+| | |_ foldcode.js
+| | |_ foldgutter.css
+| | |_ foldgutter.js
+| | |_ indent-fold.js
+| |_ hint/
+| | |_ css-hint.js
+| | |_ show-hint.css
+| | |_ show-hint.js
+| |_ lint/
+| | |_ css-lint.js
+| | |_ json-lint.js
+| | |_ lint.css
+| | |_ lint.js
+| |_ scroll/
+| | |_ annotatescrollbar.js
+| |_ search/
+| | |_ match-highlighter.js
+| | |_ matchesonscrollbar.css
+| | |_ matchesonscrollbar.js
+| | |_ searchcursor.js
+| |_ selection/
+| | |_ active-line.js
+|_ keymap/* (all files)
+|_ lib/* (all files)
+|_ mode/
+| |_ css/* (all files)
+| |_ javascript/* (all files)
+| |_ stylus/* (all files)
+|_ theme/* (all files)