qword 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/ChangeLog +22 -0
  2. package/README.md +2 -3
  3. package/assets/style.css +10 -10
  4. package/client/_clipboard.js +30 -33
  5. package/client/_clipboard.spec.js +160 -0
  6. package/client/content.d.ts +8 -0
  7. package/client/content.js +14 -0
  8. package/client/content.spec.js +69 -0
  9. package/client/create.d.ts +32 -0
  10. package/client/create.js +95 -0
  11. package/client/create.spec.js +192 -0
  12. package/client/decorations.d.ts +40 -0
  13. package/client/decorations.js +110 -0
  14. package/client/decorations.spec.js +135 -0
  15. package/client/dom.d.ts +7 -0
  16. package/client/dom.js +17 -0
  17. package/client/dom.spec.js +78 -0
  18. package/client/editor.d.ts +72 -0
  19. package/client/editor.js +164 -0
  20. package/client/editor.spec.js +307 -0
  21. package/client/events.d.ts +7 -0
  22. package/client/events.js +8 -0
  23. package/client/events.spec.js +100 -0
  24. package/client/highlight.d.ts +3 -0
  25. package/client/highlight.js +82 -0
  26. package/client/highlight.spec.js +28 -0
  27. package/client/history.d.ts +4 -0
  28. package/client/history.js +16 -0
  29. package/client/history.spec.js +57 -0
  30. package/client/index.d.ts +60 -0
  31. package/client/index.js +37 -0
  32. package/client/loadremote.js +10 -4
  33. package/client/options.d.ts +30 -0
  34. package/client/options.js +59 -0
  35. package/client/options.spec.js +157 -0
  36. package/client/position.d.ts +21 -0
  37. package/client/position.js +33 -0
  38. package/client/position.spec.js +176 -0
  39. package/client/qword.js +141 -391
  40. package/client/save.js +2 -2
  41. package/client/scroll.d.ts +10 -0
  42. package/client/scroll.js +9 -0
  43. package/client/scroll.spec.js +58 -0
  44. package/client/types.d.ts +24 -0
  45. package/dist/qword.js +175 -1
  46. package/dist/qword.js.map +1 -1
  47. package/dist-dev/qword.js +544 -660
  48. package/package.json +30 -15
  49. package/server/edit.js +8 -3
  50. package/server/edit.spec.js +101 -0
  51. package/test/errors.ts +21 -0
  52. package/test/types.ts +134 -0
  53. package/client/codemirror/show-trailing.js +0 -54
  54. package/client/codemirror/use-soft-tabs.js +0 -38
  55. package/client/set-key-map.js +0 -3
  56. package/dist/2b22a33a0a80bb926bbc.png +0 -1
  57. package/dist/8e9019bd86255ac9a618.png +0 -1
  58. package/dist/codemirror.js +0 -2
  59. package/dist/codemirror.js.map +0 -1
  60. package/dist/dword.js +0 -2
  61. package/dist/dword.js.map +0 -1
  62. package/dist-dev/2b22a33a0a80bb926bbc.png +0 -1
  63. package/dist-dev/8e9019bd86255ac9a618.png +0 -1
  64. package/dist-dev/codemirror.js +0 -104
  65. package/dist-dev/dword.js +0 -960
  66. package/modules/codemirror-emmet/dist/emmet.js +0 -48661
  67. package/modules/codemirror-emmet/dist/emmet.min.js +0 -8
  68. package/modules/codemirror-emmet/dist/emmetPlugin.js +0 -840
  69. package/modules/codemirror-emmet/dist/emmetPlugin.min.js +0 -1
  70. package/modules/codemirror-emmet/dist/index.html +0 -46
  71. package/modules/jshint/README.md +0 -114
  72. package/modules/jshint/dist/jshint-rhino.js +0 -32143
  73. package/modules/jshint/dist/jshint.js +0 -32141
  74. package/modules/jshint/package.json +0 -77
package/package.json CHANGED
@@ -1,9 +1,23 @@
1
1
  {
2
2
  "name": "qword",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "description": "Web editor based on CodeMirror 6",
5
5
  "main": "./server/index.js",
6
- "exports": "./server/index.js",
6
+ "exports": {
7
+ ".": "./server/index.js",
8
+ "./client": "./client/index.js",
9
+ "./client/editor": "./client/editor.js",
10
+ "./client/create": "./client/create.js",
11
+ "./client/content": "./client/content.js",
12
+ "./client/position": "./client/position.js",
13
+ "./client/decorations": "./client/decorations.js",
14
+ "./client/events": "./client/events.js",
15
+ "./client/scroll": "./client/scroll.js",
16
+ "./client/dom": "./client/dom.js",
17
+ "./client/options": "./client/options.js",
18
+ "./client/history": "./client/history.js",
19
+ "./client/highlight": "./client/highlight.js"
20
+ },
7
21
  "type": "module",
8
22
  "bin": {
9
23
  "qword": "bin/qword.js"
@@ -16,6 +30,7 @@
16
30
  "start:dev": "madrun start:dev",
17
31
  "lint": "madrun lint",
18
32
  "test": "madrun test",
33
+ "test:dts": "madrun test:dts",
19
34
  "coverage": "madrun coverage",
20
35
  "fresh:lint": "madrun fresh:lint",
21
36
  "lint:fresh": "madrun lint:fresh",
@@ -25,10 +40,6 @@
25
40
  "build:client:dev": "madrun build:client:dev",
26
41
  "build:start": "madrun build:start",
27
42
  "build:start:dev": "madrun build:start:dev",
28
- "cp:codemirror:modules": "madrun cp:codemirror:modules",
29
- "build-progress:codemirror": "madrun build-progress:codemirror",
30
- "build:codemirror:dev": "madrun build:codemirror:dev",
31
- "build:codemirror": "madrun build:codemirror",
32
43
  "watch:server": "madrun watch:server",
33
44
  "watch:client": "madrun watch:client",
34
45
  "watch": "madrun watch",
@@ -49,8 +60,6 @@
49
60
  "try-to-catch": "^4.0.5"
50
61
  },
51
62
  "devDependencies": {
52
- "@babel/core": "^7.0.0",
53
- "@babel/preset-env": "^7.0.0",
54
63
  "@cloudcmd/clipboard": "^2.0.0",
55
64
  "@cloudcmd/create-element": "^2.0.2",
56
65
  "@codemirror/commands": "^6.10.3",
@@ -61,14 +70,20 @@
61
70
  "@codemirror/state": "^6.6.0",
62
71
  "@codemirror/theme-one-dark": "^6.1.3",
63
72
  "@codemirror/view": "^6.42.1",
73
+ "@lezer/highlight": "^1.2.3",
64
74
  "@putout/eslint-flat": "^4.0.0",
75
+ "@putout/plugin-convert-webpack-to-rspack": "^1.0.1",
65
76
  "@replit/codemirror-vim": "^6.3.0",
66
- "babel-loader": "^10.0.0",
77
+ "@rspack/cli": "^2.2.1",
78
+ "@rspack/core": "^2.2.1",
79
+ "@supertape/loader-css": "^1.0.0",
80
+ "@supertape/loader-dom": "^1.0.0",
81
+ "@testing-library/dom": "^10.4.1",
82
+ "@uiw/codemirror-theme-nord": "^4.25.11",
67
83
  "callbackify": "^1.1.0",
68
- "clean-css-loader": "^4.1.1",
69
- "codemirror": "^5.0.0",
84
+ "check-dts": "^1.0.0",
70
85
  "css-loader": "^7.1.3",
71
- "cssnano": "^7.1.2",
86
+ "cssnano": "^9.0.2",
72
87
  "daffy": "^1.0.3",
73
88
  "emitify": "^4.0.1",
74
89
  "es6-promisify": "^7.0.0",
@@ -84,10 +99,10 @@
84
99
  "rimraf": "^6.1.2",
85
100
  "smalltalk": "^5.0.0",
86
101
  "style-loader": "^4.0.0",
102
+ "superc8": "^12.7.0",
103
+ "supertape": "^13.5.2",
104
+ "supertest": "^7.2.2",
87
105
  "try-catch": "^4.0.9",
88
- "url-loader": "^4.0.0",
89
- "webpack": "^5.1.3",
90
- "webpack-cli": "^7.0.2",
91
106
  "wraptile": "^3.0.0",
92
107
  "zipio": "^1.0.2"
93
108
  },
package/server/edit.js CHANGED
@@ -7,6 +7,8 @@ import Edit from '../json/edit.json' with {
7
7
 
8
8
  const HOME = os.homedir();
9
9
 
10
+ let configPath = `${HOME}/.qword.json`;
11
+
10
12
  export default async (req, res, next) => {
11
13
  if (req.url !== '/edit.json')
12
14
  return next();
@@ -21,6 +23,10 @@ export default async (req, res, next) => {
21
23
  res.json(data);
22
24
  };
23
25
 
26
+ export const setConfigPath = (path) => {
27
+ configPath = path;
28
+ };
29
+
24
30
  function replace(from, to) {
25
31
  for (const name of Object.keys(from)) {
26
32
  to[name] = from[name];
@@ -37,13 +43,12 @@ function copy(from) {
37
43
  }
38
44
 
39
45
  async function readEdit() {
40
- const homePath = `${HOME}/.qword.json`;
41
46
  const data = copy(Edit);
42
47
 
43
- const [error, edit] = await tryToCatch(readjson, homePath);
48
+ const [error, edit] = await tryToCatch(readjson, configPath);
44
49
 
45
50
  if (error && error.code !== 'ENOENT')
46
- throw Error(`qword --config ${homePath}: ${error.message}`);
51
+ throw Error(`qword --config ${configPath}: ${error.message}`);
47
52
 
48
53
  if (!edit)
49
54
  return data;
@@ -0,0 +1,101 @@
1
+ import {tmpdir} from 'node:os';
2
+ import {join} from 'node:path';
3
+ import {
4
+ mkdtempSync,
5
+ writeFileSync,
6
+ rmSync,
7
+ } from 'node:fs';
8
+ import {test} from 'supertape';
9
+ import request from 'supertest';
10
+ import express from 'express';
11
+ import editFn, {setConfigPath} from './edit.js';
12
+
13
+ const makeApp = () => {
14
+ const app = express();
15
+ app.use(editFn);
16
+
17
+ return app;
18
+ };
19
+
20
+ const makeTempConfig = (content) => {
21
+ const dir = mkdtempSync(join(tmpdir(), 'qword-'));
22
+ const file = join(dir, 'config.json');
23
+
24
+ writeFileSync(file, content);
25
+
26
+ return {
27
+ file,
28
+ cleanup: () => rmSync(dir, {
29
+ recursive: true,
30
+ force: true,
31
+ }),
32
+ };
33
+ };
34
+
35
+ test('edit: returns 200 for /edit.json', async (t) => {
36
+ setConfigPath(join(mkdtempSync(join(tmpdir(), 'qword-')), 'config.json'));
37
+ const response = await request(makeApp()).get('/edit.json');
38
+
39
+ t.equal(response.status, 200);
40
+ t.end();
41
+ });
42
+
43
+ test('edit: returns json content-type for /edit.json', async (t) => {
44
+ setConfigPath(join(mkdtempSync(join(tmpdir(), 'qword-')), 'config.json'));
45
+
46
+ const response = await request(makeApp()).get('/edit.json');
47
+ const result = response.headers['content-type'].includes('json');
48
+
49
+ t.ok(result);
50
+ t.end();
51
+ });
52
+
53
+ test('edit: response body has options field', async (t) => {
54
+ setConfigPath(join(mkdtempSync(join(tmpdir(), 'qword-')), 'config.json'));
55
+ const response = await request(makeApp()).get('/edit.json');
56
+
57
+ t.ok(response.body.options);
58
+ t.end();
59
+ });
60
+
61
+ test('edit: passes through non-edit.json requests', async (t) => {
62
+ setConfigPath(join(mkdtempSync(join(tmpdir(), 'qword-')), 'config.json'));
63
+
64
+ const app = makeApp();
65
+
66
+ app.get('/other', (_req, res) => res.json({
67
+ ok: true,
68
+ }));
69
+ const response = await request(app).get('/other');
70
+
71
+ t.equal(response.status, 200);
72
+ t.end();
73
+ });
74
+
75
+ test('edit: merges options from user config', async (t) => {
76
+ const {file, cleanup} = makeTempConfig(JSON.stringify({
77
+ options: {
78
+ theme: 'dark',
79
+ },
80
+ }));
81
+ setConfigPath(file);
82
+ const response = await request(makeApp()).get('/edit.json');
83
+
84
+ cleanup();
85
+
86
+ t.equal(response.body.options.theme, 'dark');
87
+ t.end();
88
+ });
89
+
90
+ test('edit: returns 404 when config read fails', async (t) => {
91
+ const {file, cleanup} = makeTempConfig('{}');
92
+ const dir = join(file, '..');
93
+
94
+ setConfigPath(dir);
95
+ const response = await request(makeApp()).get('/edit.json');
96
+
97
+ cleanup();
98
+
99
+ t.equal(response.status, 404);
100
+ t.end();
101
+ });
package/test/errors.ts ADDED
@@ -0,0 +1,21 @@
1
+ import {
2
+ createEditor,
3
+ setValue,
4
+ positionToOffset,
5
+ setOption,
6
+ } from '../client/index.js';
7
+
8
+ const view = createEditor(document.body);
9
+
10
+ // THROWS Argument of type 'number' is not assignable to parameter of type 'string'
11
+ setValue(view, 5);
12
+
13
+ // THROWS Argument of type 'number' is not assignable to parameter of type 'SourcePosition'
14
+ positionToOffset(view.state.doc, 5);
15
+
16
+ // THROWS Argument of type '"bogus"' is not assignable to parameter of type 'OptionKey'
17
+ setOption(view, 'bogus', 'dark');
18
+
19
+ // THROWS Argument of type '5' is not assignable to parameter of type 'OptionValue'
20
+ setOption(view, 'theme', 5);
21
+
package/test/types.ts ADDED
@@ -0,0 +1,134 @@
1
+ import Editor from '../client/editor.js';
2
+ import {
3
+ createEditor,
4
+ getValue,
5
+ setValue,
6
+ offsetToPosition,
7
+ positionToOffset,
8
+ posFromIndex,
9
+ indexFromPos,
10
+ getCursorIndex,
11
+ getCursor,
12
+ markText,
13
+ addLineClass,
14
+ removeLineClass,
15
+ on,
16
+ off,
17
+ getScrollInfo,
18
+ scrollTo,
19
+ getView,
20
+ refresh,
21
+ observeResize,
22
+ setOption,
23
+ clearHistory,
24
+ } from '../client/index.js';
25
+
26
+ const view = createEditor(document.body, {
27
+ value: 'const a = 3',
28
+ mode: 'javascript',
29
+ keyMap: 'default',
30
+ theme: 'default',
31
+ lineNumbers: true,
32
+ readOnly: false,
33
+ foldGutter: true,
34
+ });
35
+
36
+ // content
37
+ const value: string = getValue(view);
38
+ setValue(view, 'const b = 4');
39
+
40
+ // position
41
+ const position = offsetToPosition(view.state.doc, 10);
42
+ const offset: number = positionToOffset(view.state.doc, {
43
+ line: 0,
44
+ ch: 0,
45
+ });
46
+ const pos = posFromIndex(view, 0);
47
+ const idx = indexFromPos(view, {
48
+ line: 0,
49
+ ch: 0,
50
+ });
51
+ const cursor = getCursorIndex(view);
52
+ const {row, column} = getCursor(view);
53
+
54
+ // decorations
55
+ const handle = markText(view, {
56
+ line: 0,
57
+ ch: 0,
58
+ }, {
59
+ line: 0,
60
+ ch: 1,
61
+ }, {
62
+ className: 'hl-keyword',
63
+ });
64
+ handle.clear();
65
+ addLineClass(view, 0, 'text', 'hl-string');
66
+ removeLineClass(view, 0, 'text', 'hl-string');
67
+
68
+ // events
69
+ const binding = on(view, 'keydown', () => {});
70
+ off(view, 'keydown', () => {});
71
+
72
+ // scroll
73
+ const {left, top} = getScrollInfo(view);
74
+ scrollTo(view, left, top);
75
+
76
+ // dom
77
+ const found = getView(document.body);
78
+ refresh(view);
79
+ const unobserve = observeResize(view, document.body);
80
+ unobserve();
81
+
82
+ // options
83
+ setOption(view, 'mode', 'javascript');
84
+ setOption(view, 'theme', 'nord');
85
+ setOption(view, 'keyMap', 'vim');
86
+
87
+ // history
88
+ clearHistory(view, view._historyCompartment, false);
89
+
90
+ // Editor class
91
+ const editor = new Editor(document.body, {
92
+ value: 'const a = 3',
93
+ mode: 'javascript',
94
+ theme: 'default',
95
+ keyMap: 'default',
96
+ });
97
+ editor
98
+ .setValue('const b = 4')
99
+ .focus()
100
+ .moveCursorTo(0, 1)
101
+ .addLineClass(0, 'hl-string')
102
+ .removeLineClass(0, 'hl-string')
103
+ .setMode('javascript')
104
+ .setTheme('nord')
105
+ .setKeyMap('vim')
106
+ .setModeForPath('a.js')
107
+ .clearHistory()
108
+ .on('change', () => {})
109
+ .emit('change', {});
110
+ const editorValue: string = editor.getValue();
111
+ const editorCursor = editor.getCursor();
112
+ const editorPos = editor.posFromIndex(0);
113
+ editor.markRange({
114
+ line: 0,
115
+ ch: 0,
116
+ }, {
117
+ line: 0,
118
+ ch: 1,
119
+ }, 'className').clear();
120
+
121
+ // types compile
122
+ void value;
123
+ void position;
124
+ void offset;
125
+ void pos;
126
+ void idx;
127
+ void cursor;
128
+ void row;
129
+ void column;
130
+ void binding;
131
+ void found;
132
+ void editorValue;
133
+ void editorCursor;
134
+ void editorPos;
@@ -1,54 +0,0 @@
1
- 'use strict';
2
-
3
- /* global CodeMirror */
4
- /* global define */
5
-
6
- (function(mod) {
7
- const isFn = (a) => typeof a === 'function';
8
-
9
- if (typeof exports === 'object' && typeof module === 'object') // CommonJS
10
- mod(require('../../lib/codemirror'));
11
- else if (isFn(define) && define.amd) // AMD
12
- define(['../../lib/codemirror'], mod);
13
- else // Plain browser env
14
- mod(CodeMirror);
15
- })((CodeMirror) => {
16
- add();
17
-
18
- CodeMirror.defineOption('showTrailing', false, (cm, val, prev) => {
19
- if (prev === CodeMirror.Init)
20
- prev = false;
21
-
22
- if (prev && !val)
23
- cm.removeOverlay('trailingspace');
24
- else if (!prev && val)
25
- cm.addOverlay({
26
- token(stream) {
27
- let ret = null;
28
- const match = stream.match(/\s+$/);
29
-
30
- if (match && match[0] !== stream.string)
31
- ret = 'trailing-whitespace';
32
-
33
- stream.match(/^\s*\S/);
34
-
35
- return ret;
36
- },
37
- name: 'trailingspace',
38
- });
39
- });
40
-
41
- function add() {
42
- const style = document.createElement('style');
43
-
44
- style.setAttribute('data-name', 'js-showTrailing');
45
-
46
- style.textContent = [
47
- '.cm-trailing-whitespace {',
48
- 'color: #f80;',
49
- 'text-decoration: underline;',
50
- '}'].join('');
51
-
52
- document.head.appendChild(style);
53
- }
54
- });
@@ -1,38 +0,0 @@
1
- 'use strict';
2
-
3
- /* global CodeMirror */
4
- /* global define */
5
-
6
- (function(mod) {
7
- if (typeof exports === 'object' && typeof module === 'object') // CommonJS
8
- mod(require('../../lib/codemirror'));
9
- else if (typeof define === 'function' && define.amd) // AMD
10
- define(['../../lib/codemirror'], mod);
11
- else // Plain browser env
12
- mod(CodeMirror);
13
- })((CodeMirror) => {
14
- CodeMirror.defineOption('useSoftTabs', false, (cm) => {
15
- cm.addKeyMap({
16
- 'Tab'(cm) {
17
- let line;
18
- const sel = cm.getSelection('\n');
19
- const coupleLines = sel.length > 0;
20
- const isSelected = cm.somethingSelected();
21
-
22
- if (isSelected) {
23
- line = cm.getLine(cm.getCursor().line);
24
- // Indent only if there are multiple lines selected, or if the selection spans a full line
25
-
26
- if (coupleLines && (sel.includes('\n') || sel.length === line.length)) {
27
- cm.indentSelection('add');
28
- }
29
- } else {
30
- cm.execCommand('insertSoftTab');
31
- }
32
- },
33
- 'Shift-Tab'(cm) {
34
- cm.indentSelection('subtract');
35
- },
36
- });
37
- });
38
- });
@@ -1,3 +0,0 @@
1
- export default function(keyMap) {
2
- this.setOption('keyMap', keyMap);
3
- }
@@ -1 +0,0 @@
1
- export default "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAQAAAC1QeVaAAAAUklEQVR4XqXPYQrAIAhAYW/gXd8NJxTopVqsGEhtf+L9/ERU2k/HSMFQpKcYJeNFI9Be0LCMij8cYyjj5EHIivGBkwLfrbX3IF8PqumVmnDpEG+eDsKibPG2JwAAAABJRU5ErkJggg=="
@@ -1 +0,0 @@
1
- export default "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAQAAAC1QeVaAAAAnUlEQVR4XoWQQQ6CQAxFewjkJkMCyXgJPMk7AiYczyBeZEAX6AKctGIaN+bt+trk9wtGQc/IkhnoKGxqqiWxOSZalapWFZ6VrIUDExsN0a5JRBq9LoVOR0eEQMoEhKizXhhsn0p1sCWVo7CwOf1RytPL8CPvwuBUoHL6ugeK30CVD1TqK7V/hdpe+VNChhOzV8xWny/+xosHF8578W/Hmc1OOC3wmwAAAABJRU5ErkJggg=="