From 6a746fb879a5b4961d37d6f9fd4bfd8bd6286028 Mon Sep 17 00:00:00 2001 From: azu Date: Wed, 5 Aug 2020 09:27:02 +0900 Subject: [PATCH] feat: add textlint-script-parser --- packages/@textlint/script-parser/.gitignore | 156 ++++++++++++++++++ .../@textlint/script-parser/.mocharc.json | 5 + packages/@textlint/script-parser/LICENSE | 19 +++ packages/@textlint/script-parser/README.md | 60 +++++++ packages/@textlint/script-parser/package.json | 58 +++++++ .../script-parser/src/script-parser.ts | 20 +++ .../script-parser/test/script-parser.test.ts | 18 ++ .../script-parser/test/tsconfig.json | 11 ++ .../@textlint/script-parser/tsconfig.json | 36 ++++ .../script-parser/tsconfig.module.json | 7 + .../app/scripts/background/openDatabase.ts | 9 +- .../app/scripts/install-dialog.ts | 22 ++- packages/webextension/package.json | 1 + yarn.lock | 31 ++++ 14 files changed, 441 insertions(+), 12 deletions(-) create mode 100644 packages/@textlint/script-parser/.gitignore create mode 100644 packages/@textlint/script-parser/.mocharc.json create mode 100644 packages/@textlint/script-parser/LICENSE create mode 100644 packages/@textlint/script-parser/README.md create mode 100644 packages/@textlint/script-parser/package.json create mode 100644 packages/@textlint/script-parser/src/script-parser.ts create mode 100644 packages/@textlint/script-parser/test/script-parser.test.ts create mode 100644 packages/@textlint/script-parser/test/tsconfig.json create mode 100644 packages/@textlint/script-parser/tsconfig.json create mode 100644 packages/@textlint/script-parser/tsconfig.module.json diff --git a/packages/@textlint/script-parser/.gitignore b/packages/@textlint/script-parser/.gitignore new file mode 100644 index 0000000..a7c03ce --- /dev/null +++ b/packages/@textlint/script-parser/.gitignore @@ -0,0 +1,156 @@ +### https://raw.github.com/github/gitignore/d2c1bb2b9c72ead618c9f6a48280ebc7a8e0dff6/Node.gitignore +# 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 (https://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 +node_modules/ +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 +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# next.js build output +.next + +# nuxt.js build output +.nuxt + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + + +### https://raw.github.com/github/gitignore/d2c1bb2b9c72ead618c9f6a48280ebc7a8e0dff6/Global/JetBrains.gitignore + +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/modules.xml +# .idea/*.iml +# .idea/modules + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + + +# Build files +/lib +/module diff --git a/packages/@textlint/script-parser/.mocharc.json b/packages/@textlint/script-parser/.mocharc.json new file mode 100644 index 0000000..b453523 --- /dev/null +++ b/packages/@textlint/script-parser/.mocharc.json @@ -0,0 +1,5 @@ +{ + "require": [ + "ts-node-test-register" + ] +} \ No newline at end of file diff --git a/packages/@textlint/script-parser/LICENSE b/packages/@textlint/script-parser/LICENSE new file mode 100644 index 0000000..35b018c --- /dev/null +++ b/packages/@textlint/script-parser/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2020 azu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/@textlint/script-parser/README.md b/packages/@textlint/script-parser/README.md new file mode 100644 index 0000000..7b6c2d8 --- /dev/null +++ b/packages/@textlint/script-parser/README.md @@ -0,0 +1,60 @@ +# @textlint/script-parser + +A parser for textlint script + +## Install + +Install with [npm](https://www.npmjs.com/): + + npm install @textlint/script-parser + +## Usage + +`textlintScriptContent` should includes `/**! textlint: JSONstring */`; + +```ts +import { parseMetadata } from "@textlint/script-parser"; + +const scriptContent = `/*! textlinteditor: {"name":"example","namespace":"https://github.com/textlint/editor","config":{"rules":{"preset-ja-technical-writing":true},"plugins":{"@textlint/text":true,"@textlint/markdown":true}}} */ +self.textlint=function(e){var t={};function n(r){if(t[r])return t[r].exports;var i=t[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)n.d(r,i,function(t){return e[t]}.bind(null,i));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=465)}([function(e,t,n){"use strict";(function(t){var r=n(37);`; +const metadata = parseMetadata(scriptContent); +assert.deepStrictEqual(metadata, { + name: "example", + namespace: "https://github.com/textlint/editor", + config: { + rules: { "preset-ja-technical-writing": true }, + plugins: { "@textlint/text": true, "@textlint/markdown": true } + } +}); +``` + +## Changelog + +See [Releases page](https://github.com/textlint/editor/releases). + +## Running tests + +Install devDependencies and Run `npm test`: + + npm test + +## Contributing + +Pull requests and stars are always welcome. + +For bugs and feature requests, [please create an issue](https://github.com/textlint/editor/issues). + +1. Fork it! +2. Create your feature branch: `git checkout -b my-new-feature` +3. Commit your changes: `git commit -am 'Add some feature'` +4. Push to the branch: `git push origin my-new-feature` +5. Submit a pull request :D + +## Author + +- [github/azu](https://github.com/azu) +- [twitter/azu_re](https://twitter.com/azu_re) + +## License + +MIT © azu diff --git a/packages/@textlint/script-parser/package.json b/packages/@textlint/script-parser/package.json new file mode 100644 index 0000000..36b86e4 --- /dev/null +++ b/packages/@textlint/script-parser/package.json @@ -0,0 +1,58 @@ +{ + "name": "@textlint/script-parser", + "version": "0.6.0", + "description": "A parser for textlint script", + "keywords": [ + "textlint" + ], + "homepage": "https://github.com/textlint/editor/tree/master/packages/@textlint/script-parser/", + "bugs": { + "url": "https://github.com/textlint/editor/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/textlint/editor.git" + }, + "license": "MIT", + "author": "azu", + "sideEffects": false, + "main": "lib/script-parser.js", + "module": "module/script-parser.js", + "types": "lib/script-parser.d.ts", + "directories": { + "lib": "lib", + "test": "test" + }, + "files": [ + "bin/", + "lib/", + "module" + ], + "scripts": { + "build": "tsc -p . && tsc --project ./tsconfig.module.json", + "clean": "rimraf lib/ module/", + "prettier": "prettier --write \"**/*.{js,jsx,ts,tsx,css}\"", + "prepublish": "npm run --if-present build", + "test": "mocha \"test/**/*.ts\"", + "watch": "tsc -p . --watch" + }, + "prettier": { + "printWidth": 120, + "singleQuote": false, + "tabWidth": 4, + "trailingComma": "none" + }, + "devDependencies": { + "@types/mocha": "^8.0.1", + "@types/node": "^14.0.27", + "mocha": "^8.1.1", + "prettier": "^2.0.5", + "rimraf": "^3.0.2", + "ts-node": "^8.10.2", + "ts-node-test-register": "^8.0.1", + "typescript": "^3.9.7" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/@textlint/script-parser/src/script-parser.ts b/packages/@textlint/script-parser/src/script-parser.ts new file mode 100644 index 0000000..17874be --- /dev/null +++ b/packages/@textlint/script-parser/src/script-parser.ts @@ -0,0 +1,20 @@ +export type TextlintScriptMetadata = { + name: string; + namespace: string; +}; +export type TextlintScriptParseResult = { + metadata: TextlintScriptMetadata; +}; +export const parseMetadata = (scriptContent: string): TextlintScriptMetadata => { + const metadataPattenr = /\/*! textlinteditor: (.*)\*\//; + const match = scriptContent.match(metadataPattenr); + if (!match) { + throw new Error("Can not read metadata"); + } + try { + return JSON.parse(match[1]); + } catch (error) { + console.error(error); + throw new Error("Can not parse metadata. It should be JSON format."); + } +}; diff --git a/packages/@textlint/script-parser/test/script-parser.test.ts b/packages/@textlint/script-parser/test/script-parser.test.ts new file mode 100644 index 0000000..0d11457 --- /dev/null +++ b/packages/@textlint/script-parser/test/script-parser.test.ts @@ -0,0 +1,18 @@ +import assert from "assert"; +import { parseMetadata } from "../src/script-parser"; + +describe("script-parser", function () { + it("should parse comment", () => { + const scriptContent = `/*! textlinteditor: {"name":"example","namespace":"https://github.com/textlint/editor","config":{"rules":{"preset-ja-technical-writing":true},"plugins":{"@textlint/text":true,"@textlint/markdown":true}}} */ +self.textlint=function(e){var t={};function n(r){if(t[r])return t[r].exports;var i=t[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)n.d(r,i,function(t){return e[t]}.bind(null,i));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=465)}([function(e,t,n){"use strict";(function(t){var r=n(37);`; + const metadata = parseMetadata(scriptContent); + assert.deepStrictEqual(metadata, { + name: "example", + namespace: "https://github.com/textlint/editor", + config: { + rules: { "preset-ja-technical-writing": true }, + plugins: { "@textlint/text": true, "@textlint/markdown": true } + } + }); + }); +}); diff --git a/packages/@textlint/script-parser/test/tsconfig.json b/packages/@textlint/script-parser/test/tsconfig.json new file mode 100644 index 0000000..cb359a9 --- /dev/null +++ b/packages/@textlint/script-parser/test/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "declaration": false, + "noEmit": true + }, + "include": [ + "../src/**/*", + "./**/*" + ] +} \ No newline at end of file diff --git a/packages/@textlint/script-parser/tsconfig.json b/packages/@textlint/script-parser/tsconfig.json new file mode 100644 index 0000000..e1d6a02 --- /dev/null +++ b/packages/@textlint/script-parser/tsconfig.json @@ -0,0 +1,36 @@ +{ + "compilerOptions": { + /* Basic Options */ + "module": "commonjs", + "moduleResolution": "node", + "esModuleInterop": true, + "newLine": "LF", + "outDir": "./lib/", + "target": "es5", + "sourceMap": true, + "declaration": true, + "jsx": "preserve", + "lib": [ + "esnext", + "dom" + ], + /* Strict Type-Checking Options */ + "strict": true, + /* Additional Checks */ + /* Report errors on unused locals. */ + "noUnusedLocals": true, + /* Report errors on unused parameters. */ + "noUnusedParameters": true, + /* Report error when not all code paths in function return a value. */ + "noImplicitReturns": true, + /* Report errors for fallthrough cases in switch statement. */ + "noFallthroughCasesInSwitch": true + }, + "include": [ + "src/**/*" + ], + "exclude": [ + ".git", + "node_modules" + ] +} \ No newline at end of file diff --git a/packages/@textlint/script-parser/tsconfig.module.json b/packages/@textlint/script-parser/tsconfig.module.json new file mode 100644 index 0000000..c94d549 --- /dev/null +++ b/packages/@textlint/script-parser/tsconfig.module.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "ESNext", + "outDir": "./module/", + } +} \ No newline at end of file diff --git a/packages/webextension/app/scripts/background/openDatabase.ts b/packages/webextension/app/scripts/background/openDatabase.ts index aee214f..1427abd 100644 --- a/packages/webextension/app/scripts/background/openDatabase.ts +++ b/packages/webextension/app/scripts/background/openDatabase.ts @@ -1,12 +1,13 @@ -import { openDB, DBSchema, StoreValue } from "idb"; +import { DBSchema, openDB, StoreValue } from "idb"; import minimatch from "minimatch"; interface TextlintJsDB extends DBSchema { scripts: { value: { name: string; + namespace: string; code: string; - pattern: string; + matchPattern: string; }; key: string; indexes: { "by-name": string }; @@ -31,9 +32,7 @@ export async function openDatabase() { async findScriptsWithPatten(url: string) { const scripts = await db.getAll("scripts"); return scripts.filter((script) => { - const minimatch1 = minimatch(url, script.pattern); - console.log(minimatch1, url, script.pattern); - return minimatch1; + return minimatch(url, script.matchPattern); }); }, async deleteScript(scriptName: ScriptValue["name"]) { diff --git a/packages/webextension/app/scripts/install-dialog.ts b/packages/webextension/app/scripts/install-dialog.ts index 861faba..c5f89b6 100644 --- a/packages/webextension/app/scripts/install-dialog.ts +++ b/packages/webextension/app/scripts/install-dialog.ts @@ -2,6 +2,7 @@ import { createEndpoint } from "comlink-extension"; import * as Comlink from "comlink"; import type { backgroundExposedObject } from "./background"; import { browser } from "webextension-polyfill-ts"; +import { parseMetadata } from "@textlint/script-parser"; const port = Comlink.wrap(createEndpoint(browser.runtime.connect())); @@ -13,13 +14,20 @@ async function installHandler() { } const scriptURL = decodeURIComponent(script); const content = await fetch(scriptURL).then((res) => res.text()); - console.log(content); - // Save to DB - await port.addScript({ - name: location.href, - code: content, - pattern: "**/*" - }); + console.log("[InstallDialog]", content.slice(500)); + try { + const parseResult = parseMetadata(content); + console.log("[InstallDialog] metada", parseResult); + // Save to DB + await port.addScript({ + name: parseResult.name, + namespace: parseResult.namespace, + code: content, + matchPattern: "**/*" + }); + } catch (error) { + console.error("[InstallDialog]", error); + } } document.querySelector("#js-install-button")?.addEventListener("click", installHandler); diff --git a/packages/webextension/package.json b/packages/webextension/package.json index d52ab22..ed15df7 100644 --- a/packages/webextension/package.json +++ b/packages/webextension/package.json @@ -26,6 +26,7 @@ "@react-stately/data": "^3.1.0", "@adobe/react-spectrum": "^3.1.0", "@textlint/types": "^1.4.5", + "@textlint/script-parser": "^0.6.0", "@webcomponents/custom-elements": "^1.4.2", "comlink": "^4.3.0", "comlink-extension": "^1.0.8", diff --git a/yarn.lock b/yarn.lock index bfaef47..ce26e91 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8886,6 +8886,37 @@ mocha@^8.1.0: yargs-parser "13.1.2" yargs-unparser "1.6.1" +mocha@^8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-8.1.1.tgz#1de1ba4e9a2c955d96b84e469d7540848223592d" + integrity sha512-p7FuGlYH8t7gaiodlFreseLxEmxTgvyG9RgPHODFPySNhwUehu8NIb0vdSt3WFckSneswZ0Un5typYcWElk7HQ== + dependencies: + ansi-colors "4.1.1" + browser-stdout "1.3.1" + chokidar "3.3.1" + debug "3.2.6" + diff "4.0.2" + escape-string-regexp "1.0.5" + find-up "4.1.0" + glob "7.1.6" + growl "1.10.5" + he "1.2.0" + js-yaml "3.13.1" + log-symbols "3.0.0" + minimatch "3.0.4" + ms "2.1.2" + object.assign "4.1.0" + promise.allsettled "1.0.2" + serialize-javascript "4.0.0" + strip-json-comments "3.0.1" + supports-color "7.1.0" + which "2.0.2" + wide-align "1.1.3" + workerpool "6.0.0" + yargs "13.3.2" + yargs-parser "13.1.2" + yargs-unparser "1.6.1" + modify-values@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022"