Merge pull request #18 from github/typescript

Convert project to Typescript
This commit is contained in:
Kristján Oddsson 2020-04-27 13:57:41 +01:00 коммит произвёл GitHub
Родитель a82d005df4 94b2d3ce1b
Коммит 75043852f4
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
13 изменённых файлов: 1713 добавлений и 3660 удалений

Просмотреть файл

@ -1,18 +0,0 @@
{
"env": {
"esm": {
"presets": ["github"]
},
"umd": {
"plugins": [
["@babel/plugin-transform-modules-umd", {
"globals": {
"selector-set": "SelectorSet"
}
}]
],
"moduleId": "remoteForm",
"presets": ["github"]
}
}
}

Просмотреть файл

@ -1,12 +1,20 @@
{ {
"parser": "@typescript-eslint/parser",
"extends": [ "extends": [
"plugin:github/es6", "plugin:github/es6",
"plugin:github/browser", "plugin:github/browser",
"plugin:github/flow" "plugin:github/typescript"
], ],
"rules": {
"@typescript-eslint/no-non-null-assertion": "off"
},
"overrides": [ "overrides": [
{ {
"files": "test/**/*.js", "files": "test/**/*.js",
"parser": "espree",
"parserOptions": {
"ecmaVersion": 2020
},
"rules": { "rules": {
"flowtype/require-valid-file-annotation": "off", "flowtype/require-valid-file-annotation": "off",
"github/unescaped-html-literal": "off", "github/unescaped-html-literal": "off",

Просмотреть файл

@ -1,11 +0,0 @@
[ignore]
[include]
[libs]
[lints]
[options]
[strict]

1
CODEOWNERS Normal file
Просмотреть файл

@ -0,0 +1 @@
* @github/web-systems-reviewers

32
index.d.ts поставляемый
Просмотреть файл

@ -1,32 +0,0 @@
type Kicker = {
text: () => Promise<SimpleResponse>,
json: () => Promise<SimpleResponse>,
html: () => Promise<SimpleResponse>
}
type SimpleRequest = {
method: string,
url: string,
body: FormData | null,
headers: Headers
}
export type SimpleResponse = {
url: string,
status: number,
statusText: string,
headers: Headers,
text: string,
json: {[key: string]: any},
html: DocumentFragment
}
export type ErrorWithResponse = {
response: SimpleResponse
}
export type RemoteFormHandler = (form: HTMLFormElement, kicker: Kicker, req: SimpleRequest) => void | Promise<void>;
export function afterRemote(fn: (form: HTMLFormElement) => void | Promise<void>): void;
export function beforeRemote(fn: (form: HTMLFormElement) => void | Promise<void>): void;
export function remoteForm(selector: string, fn: RemoteFormHandler): void;
export function remoteUninstall(selector: string, fn: RemoteFormHandler): void;

5070
package-lock.json сгенерированный

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -4,24 +4,22 @@
"description": "Decorator that will submit a form over AJAX", "description": "Decorator that will submit a form over AJAX",
"repository": "github/remote-form", "repository": "github/remote-form",
"files": [ "files": [
"dist", "dist"
"index.d.ts"
], ],
"main": "dist/index.umd.js", "main": "dist/index.js",
"module": "dist/index.esm.js", "module": "dist/index.js",
"types:": "index.d.ts", "types:": "dist/index.d.ts",
"scripts": { "scripts": {
"clean": "rm -rf dist", "clean": "rm -rf dist",
"lint": "github-lint", "lint": "github-lint",
"prebuild": "npm run clean && npm run lint && mkdir dist", "prebuild": "npm run clean && npm run lint && mkdir dist",
"build-umd": "BABEL_ENV=umd babel src/index.js -o dist/index.umd.js", "build": "tsc",
"build-esm": "BABEL_ENV=esm babel src/index.js -o dist/index.esm.js",
"build": "npm run build-umd && npm run build-esm && cp src/index.js.flow dist/index.umd.js.flow && cp src/index.js.flow dist/index.esm.js.flow",
"pretest": "npm run build", "pretest": "npm run build",
"test": "karma start test/karma.config.js", "test": "karma start test/karma.config.js",
"prepublishOnly": "npm run build", "prepublishOnly": "npm run build",
"postpublish": "npm publish --ignore-scripts --@github:registry='https://npm.pkg.github.com'" "postpublish": "npm publish --ignore-scripts --@github:registry='https://npm.pkg.github.com'"
}, },
"prettier": "@github/prettier-config",
"keywords": [ "keywords": [
"decorator", "decorator",
"remote-form", "remote-form",
@ -32,21 +30,17 @@
], ],
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.7.0", "@github/prettier-config": "0.0.4",
"@babel/core": "^7.7.0",
"@babel/plugin-transform-modules-commonjs": "^7.7.0",
"@babel/plugin-transform-modules-umd": "^7.7.0",
"babel-preset-github": "^3.2.1",
"chai": "^4.2.0", "chai": "^4.2.0",
"eslint": "^6.6.0", "eslint": "^6.8.0",
"eslint-plugin-github": "^3.2.1", "eslint-plugin-github": "^3.4.1",
"flow-bin": "^0.102.0", "karma": "^5.0.2",
"karma": "^4.4.1",
"karma-chai": "^0.1.0", "karma-chai": "^0.1.0",
"karma-chrome-launcher": "^3.1.0", "karma-chrome-launcher": "^3.1.0",
"karma-mocha": "^1.3.0", "karma-mocha": "^2.0.0",
"karma-mocha-reporter": "^2.2.5", "karma-mocha-reporter": "^2.2.5",
"mocha": "^6.2.2" "mocha": "^7.1.1",
"typescript": "^3.8.3"
}, },
"dependencies": {} "dependencies": {}
} }

Просмотреть файл

@ -1,2 +0,0 @@
/* @flow strict */
module.exports = require('eslint-plugin-github/prettier.config')

Просмотреть файл

@ -1,30 +0,0 @@
/* @flow strict */
type Kicker = {
text: () => Promise<SimpleResponse>,
json: () => Promise<SimpleResponse>,
html: () => Promise<SimpleResponse>
}
type SimpleRequest = {
method: string,
url: string,
body: ?FormData,
headers: Headers
}
export type SimpleResponse = {
url: string,
status: number,
statusText: string,
headers: Headers,
text: string,
json: {[string]: any},
html: DocumentFragment
}
export type RemoteFormHandler = (form: HTMLFormElement, kicker: Kicker, req: SimpleRequest) => void | Promise<void>;
declare export function afterRemote(fn: (form: HTMLFormElement) => void | Promise<void>): void;
declare export function beforeRemote(fn: (form: HTMLFormElement) => void | Promise<void>): void;
declare export function remoteForm(selector: string, fn: RemoteFormHandler): void;
declare export function remoteUninstall(selector: string, fn: RemoteFormHandler): void;

Просмотреть файл

@ -1,5 +1,3 @@
/* @flow strict */
// Parse HTML text into document fragment. // Parse HTML text into document fragment.
function parseHTML(document: Document, html: string): DocumentFragment { function parseHTML(document: Document, html: string): DocumentFragment {
const template = document.createElement('template') const template = document.createElement('template')
@ -19,61 +17,59 @@ function serialize(form: HTMLFormElement): string {
class ErrorWithResponse extends Error { class ErrorWithResponse extends Error {
response: SimpleResponse response: SimpleResponse
constructor(message, response) { constructor(message: string, response: SimpleResponse) {
super(message) super(message)
this.response = response this.response = response
} }
} }
function makeDeferred<T>(): [Promise<T>, () => T, () => T] { function makeDeferred<T>(): [Promise<T>, () => void, () => void] {
let resolve let resolve: () => void
let reject let reject: () => void
const promise = new Promise(function (_resolve, _reject) { const promise = new Promise(function (_resolve, _reject) {
resolve = _resolve resolve = _resolve
reject = _reject reject = _reject
}) })
// eslint-disable-next-line flowtype/no-flow-fix-me-comments return [promise as Promise<T>, resolve!, reject!]
// $FlowFixMe
return [promise, resolve, reject]
} }
type SimpleRequest = { interface SimpleRequest {
method: string, method: string
url: string, url: string
body: ?FormData, body: FormData | null
headers: Headers headers: Headers
} }
export type SimpleResponse = { export interface SimpleResponse {
url: string, url: string
status: number, status: number
statusText: string, statusText: string
headers: Headers, headers: Headers
text: string, text: string
// eslint-disable-next-line flowtype/no-weak-types json: {[key: string]: unknown}
json: {[string]: any},
html: DocumentFragment html: DocumentFragment
} }
type Kicker = { interface Kicker {
text: () => Promise<SimpleResponse>, text: () => Promise<SimpleResponse>
json: () => Promise<SimpleResponse>, json: () => Promise<SimpleResponse>
html: () => Promise<SimpleResponse> html: () => Promise<SimpleResponse>
} }
export type RemoteFormHandler = (form: HTMLFormElement, kicker: Kicker, req: SimpleRequest) => void export type RemoteFormHandler = (form: HTMLFormElement, kicker: Kicker, req: SimpleRequest) => void
let formHandlers: Map<string, RemoteFormHandler[]> let formHandlers: Map<string, RemoteFormHandler[]>
type Handler = (form: HTMLFormElement) => void
const afterHandlers = [] const afterHandlers: Handler[] = []
const beforeHandlers = [] const beforeHandlers: Handler[] = []
export function afterRemote(fn: (form: HTMLFormElement) => mixed) { export function afterRemote(fn: Handler) {
afterHandlers.push(fn) afterHandlers.push(fn)
} }
export function beforeRemote(fn: (form: HTMLFormElement) => mixed) { export function beforeRemote(fn: Handler) {
beforeHandlers.push(fn) beforeHandlers.push(fn)
} }
@ -89,7 +85,10 @@ export function remoteForm(selector: string, fn: RemoteFormHandler) {
export function remoteUninstall(selector: string, fn: RemoteFormHandler) { export function remoteUninstall(selector: string, fn: RemoteFormHandler) {
if (formHandlers) { if (formHandlers) {
const handlers = formHandlers.get(selector) || [] const handlers = formHandlers.get(selector) || []
formHandlers.set(selector, handlers.filter(x => x !== fn)) formHandlers.set(
selector,
handlers.filter(x => x !== fn)
)
} }
} }
@ -115,11 +114,11 @@ function handleSubmit(event: Event) {
} }
const req = buildRequest(form) const req = buildRequest(form)
const [kickerPromise, ultimateResolve, ultimateReject] = makeDeferred() const [kickerPromise, ultimateResolve, ultimateReject] = makeDeferred<SimpleResponse>()
event.preventDefault() event.preventDefault()
processHandlers(matches, form, req, kickerPromise).then( processHandlers(matches, form, req, kickerPromise).then(
async performAsyncSubmit => { async (performAsyncSubmit: unknown) => {
if (performAsyncSubmit) { if (performAsyncSubmit) {
for (const handler of beforeHandlers) { for (const handler of beforeHandlers) {
await handler(form) await handler(form)
@ -128,6 +127,7 @@ function handleSubmit(event: Event) {
// TODO: ensure that these exceptions are processed by our global error handler // TODO: ensure that these exceptions are processed by our global error handler
remoteSubmit(req) remoteSubmit(req)
.then(ultimateResolve, ultimateReject) .then(ultimateResolve, ultimateReject)
// eslint-disable-next-line @typescript-eslint/no-empty-function
.catch(() => {}) .catch(() => {})
.then(() => { .then(() => {
for (const handler of afterHandlers) { for (const handler of afterHandlers) {
@ -139,7 +139,7 @@ function handleSubmit(event: Event) {
form.submit() form.submit()
} }
}, },
err => { (err: Error) => {
// TODO: special "cancel" error object to halt processing and avoid // TODO: special "cancel" error object to halt processing and avoid
// submitting the form // submitting the form
form.submit() form.submit()
@ -202,7 +202,7 @@ function buildRequest(form: HTMLFormElement): SimpleRequest {
return req return req
} }
async function remoteSubmit(req): Promise<SimpleResponse> { async function remoteSubmit(req: SimpleRequest): Promise<SimpleResponse> {
const response = await window.fetch(req.url, { const response = await window.fetch(req.url, {
method: req.method, method: req.method,
body: req.body !== null ? req.body : undefined, body: req.body !== null ? req.body : undefined,
@ -217,7 +217,7 @@ async function remoteSubmit(req): Promise<SimpleResponse> {
headers: response.headers, headers: response.headers,
text: '', text: '',
get json() { get json() {
// eslint-disable-next-line no-shadow // eslint-disable-next-line no-shadow, @typescript-eslint/no-this-alias
const response: SimpleResponse = this const response: SimpleResponse = this
const data = JSON.parse(response.text) const data = JSON.parse(response.text)
delete response.json delete response.json
@ -225,7 +225,7 @@ async function remoteSubmit(req): Promise<SimpleResponse> {
return response.json return response.json
}, },
get html() { get html() {
// eslint-disable-next-line no-shadow // eslint-disable-next-line no-shadow, @typescript-eslint/no-this-alias
const response: SimpleResponse = this const response: SimpleResponse = this
delete response.html delete response.html

Просмотреть файл

@ -24,7 +24,10 @@ module.exports = function(config) {
config.set({ config.set({
basePath: '..', basePath: '..',
frameworks: ['mocha', 'chai'], frameworks: ['mocha', 'chai'],
files: [{pattern: 'dist/index.esm.js', type: 'module'}, {pattern: 'test/test.js', type: 'module'}], files: [
{pattern: 'dist/index.js', type: 'module'},
{pattern: 'test/test.js', type: 'module'}
],
reporters: ['mocha'], reporters: ['mocha'],
port: 9876, port: 9876,
colors: true, colors: true,

Просмотреть файл

@ -1,4 +1,4 @@
import {remoteForm as _remoteForm, remoteUninstall} from '../dist/index.esm.js' import {remoteForm as _remoteForm, remoteUninstall} from '../dist/index.js'
describe('remoteForm', function () { describe('remoteForm', function () {
let htmlForm let htmlForm
@ -102,6 +102,7 @@ describe('remoteForm', function() {
event.preventDefault() event.preventDefault()
} }
const originalMochaError = window.onerror const originalMochaError = window.onerror
// eslint-disable-next-line @typescript-eslint/no-empty-function
window.onerror = function () {} window.onerror = function () {}
window.addEventListener('error', errorHandler) window.addEventListener('error', errorHandler)

17
tsconfig.json Normal file
Просмотреть файл

@ -0,0 +1,17 @@
{
"include": ["src"],
"compilerOptions": {
"strict": true,
"module": "ESNext",
"esModuleInterop": true,
"lib": ["es6", "dom", "dom.iterable"],
"target": "ES2020",
"moduleResolution": "node",
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"forceConsistentCasingInFileNames": true,
"rootDir": "src",
"outDir": "dist"
}
}