From 37931f804a50854d9ed0f714994ce5759b67455f Mon Sep 17 00:00:00 2001 From: Shigma <1700011071@pku.edu.cn> Date: Sun, 30 Sep 2018 00:46:46 +0800 Subject: [PATCH 01/35] emmmmm --- packages/core/src/index.ts | 151 +++++++++++++++++++++---- packages/inline/src/index.ts | 109 ++++++------------ packages/lexer/src/index.ts | 206 +++++++++++++++-------------------- packages/parser/src/index.ts | 4 +- packages/syntax/src/index.ts | 4 +- 5 files changed, 252 insertions(+), 222 deletions(-) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 9f71c60..84b9b42 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,7 +1,7 @@ export type StringLike = string | RegExp export type LexerConfig = Record -export type LexerMacros = Record +export type LexerMacros = Record export type TokenLike = string | LexerToken export interface LexerToken { @@ -15,14 +15,14 @@ export interface LexerToken { export type LexerRule< S extends StringLike = RegExp, - T extends LexerInstance = LexerInstance, + T extends Lexer = Lexer, R extends RegExpExecArray = RegExpExecArray, > = LexerIncludeRule | LexerRegexRule export interface LexerIncludeRule { include: string } export interface LexerRegexRule< S extends StringLike = RegExp, - T extends LexerInstance = LexerInstance, + T extends Lexer = Lexer, R extends RegExpExecArray = RegExpExecArray, > { /** the regular expression to execute */ @@ -61,12 +61,13 @@ export interface LexerRegexRule< eol?: boolean } -/** Transform a string-like object into a raw string. */ +/** transform a string-like object into a raw string */ export function getString(string: StringLike): string { return string instanceof RegExp ? string.source : string } -export function parseRule(rule: LexerRule, macros: LexerMacros = {}): LexerRule { +/** transform lexer rules with string into ones with regexp */ +export function parseRule(rule: LexerRule, macros: LexerMacros = {}): LexerRule { if (!('include' in rule)) { if (rule.regex === undefined) { rule.regex = /(?=[\s\S])/ @@ -94,20 +95,6 @@ export function parseRule(rule: LexerRule, macros: LexerMacros = {}) return rule as LexerRule } -export interface LexerInstance { - config: LexerConfig - parse(source: string): any -} - -export interface InlineLexerResult { - index: number - output: string -} - -export interface InlineLexerInstance extends LexerInstance { - parse(source: string): InlineLexerResult -} - export enum MatchStatus { /** No match was found */ NO_MATCH, @@ -116,3 +103,129 @@ export enum MatchStatus { /** Found match and pop */ POP, } + +export interface LexerResult { + /** current index of the source string */ + index: number + /** output string or array */ + output: R +} + +export interface LexerMeta extends Partial> { + /** record where the match starts */ + start?: number + /** a copy of source string */ + source?: string + /** a string collecting unmatch chars */ + unmatch?: string + /** whether running at top level */ + isTopLevel?: boolean + /** current lexing context */ + context?: LexerRegexRule[] +} + +export abstract class Lexer { + meta: LexerMeta + config: LexerConfig + + constructor(config: LexerConfig) { + this.config = config || {} + } + + initialize?(...args: any[]): void | LexerResult + getCapture?(rule: LexerRegexRule, capture: RegExpExecArray): RegExpExecArray + getContent?(rule: LexerRegexRule): TokenLike[] + pushToken?(rule: LexerRegexRule, capture: RegExpExecArray, content: TokenLike[]): void + pushUnmatch?(): void + + run(source: string, isTopLevel?: boolean, ...args: any[]): LexerResult { + // store meta data from lower level + const _meta = this.meta + this.meta = { + source, + isTopLevel, + index: 0, + unmatch: '', + } + + // initialize or simply get the result + const final = this.initialize(...args) + if (final) return final + + // walk through the source string + while (this.meta.source) { + let status: MatchStatus = MatchStatus.NO_MATCH + for (const rule of this.meta.context) { + // Step 1: test before matching + if (rule.top_level && !this.meta.isTopLevel) continue + if (rule.context_begins && this.meta.index) continue + + let test = rule.test + if (typeof test === 'string') { + if (test.charAt(0) === '!') { + test = !this.config[test.slice(1)] + } else { + test = !!this.config[test] + } + } else if (typeof test === 'function') { + test = !!test.call(this, this.config) + } + if (!test) continue + + // Step 2: exec regex and get capture + const match = rule.regex.exec(this.meta.source) + if (!match) continue + this.meta.source = this.meta.source.slice(match[0].length) + this.meta.start = this.meta.index + this.meta.index += match[0].length + const capture = this.getCapture ? this.getCapture(rule, match) : match + + // Step 3: reset match status + status = rule.pop ? MatchStatus.POP : MatchStatus.CONTINUE + + // Step 4: get inner tokens + const content = rule.push && this.getContent ? this.getContent(rule) : [] + + // Step 5: detect endless loop + if (!rule.pop && this.meta.start === this.meta.index) { + throw new Error(`Endless loop at '${ + this.meta.source.slice(0, 10) + } ${ + this.meta.source.length > 10 ? '...' : '' + }'.`) + } + + // Step 6: handle unmatched chars + if (this.pushUnmatch && this.meta.unmatch) { + this.pushUnmatch() + this.meta.unmatch = '' + } + + // Step 7: push generated token + this.pushToken(rule, capture, content) + + // Step 8: break loop + break + } + + if (status === MatchStatus.POP) break + if (status === MatchStatus.NO_MATCH) { + this.meta.unmatch += this.meta.source.charAt(0) + this.meta.source = this.meta.source.slice(1) + this.meta.index += 1 + } + } + + // handle ramaining unmatched chars + if (this.pushUnmatch && this.meta.unmatch) this.pushUnmatch() + + const result: LexerResult = { + index: this.meta.index, + output: this.meta.output, + } + + // restore meta data for lower level + this.meta = _meta + return result + } +} diff --git a/packages/inline/src/index.ts b/packages/inline/src/index.ts index b52b68d..f49c762 100644 --- a/packages/inline/src/index.ts +++ b/packages/inline/src/index.ts @@ -1,13 +1,15 @@ import { + Lexer, + parseRule, StringLike, + LexerResult, LexerConfig, LexerRegexRule, - InlineLexerInstance, - InlineLexerResult, - MatchStatus, - parseRule, } from '@marklet/core' +type InlineLexerRule = LexerRegexRule +export type InlineLexerContext = LexerRegexRule[] + class InlineCapture extends Array implements RegExpExecArray { index: number input: string @@ -22,91 +24,42 @@ class InlineCapture extends Array implements RegExpExecArray { get inner(): string { const match = this.reverse().find(item => !!item) - return match ? this.lexer.parse(match).output : '' + return match ? this.lexer.run(match).output : '' } } -type InlineLexerRule = LexerRegexRule - -export type InlineLexerRules = InlineLexerRule[] - -export class InlineLexer implements InlineLexerInstance { - config: LexerConfig +export class InlineLexer extends Lexer { private rules: InlineLexerRule[] - constructor(rules: InlineLexerRules, config: LexerConfig = {}) { - this.rules = rules.map(rule => parseRule(rule) as InlineLexerRule) - this.config = config || {} + constructor(context: InlineLexerContext, config: LexerConfig) { + super(config) + this.rules = context.map(rule => parseRule(rule) as InlineLexerRule) } - private _parse(source: string): InlineLexerResult { - let index = 0, unmatch = '', output = '' - while (source) { - let status: MatchStatus = MatchStatus.NO_MATCH - for (const rule of this.rules) { - if (rule.context_begins && index) continue - - // test - let test = rule.test - if (typeof test === 'string') { - if (test.charAt(0) === '!') { - test = !this.config[test.slice(1)] - } else { - test = !!this.config[test] - } - } else if (typeof test === 'function') { - test = test.call(this, this.config) - } - if (!test) continue - - // regex - const match = rule.regex.exec(source) - if (!match) continue - if (!match[0].length && !rule.pop) { - throw new Error(`Endless loop at '${ - source.slice(0, 10) - } ${ - source.length > 10 ? '...' : '' - }'.`) - } - const capture = new InlineCapture(this, match) - source = source.slice(capture[0].length) - index += capture[0].length - - // pop - status = rule.pop ? MatchStatus.POP : MatchStatus.CONTINUE - - // resolve unmatch - if (unmatch) { - output += unmatch - unmatch = '' - } - - // token - let token = rule.token - if (typeof token === 'function') { - token = token.call(this, capture) - } else if (token === undefined) { - token = capture[0] - } - output += token + initialize() { + this.meta.output = '' + this.meta.context = this.rules + } - break - } + getCapture(rule: InlineLexerRule, capture: RegExpExecArray) { + return new InlineCapture(this, capture) + } - if (status === MatchStatus.POP) break - if (status === MatchStatus.NO_MATCH) { - unmatch += source.charAt(0) - source = source.slice(1) - index += 1 - } + pushToken(rule: InlineLexerRule, capture: InlineCapture) { + let token = rule.token + if (typeof token === 'function') { + token = token.call(this, capture) + } else if (token === undefined) { + token = capture[0] } + this.meta.output += token + } - if (unmatch) output += unmatch - return { index, output } + pushUnmatch() { + this.meta.output += this.meta.unmatch } - parse(source: string): InlineLexerResult { - return this._parse(source.replace(/\r\n/g, '\n')) + parse(source: string): LexerResult { + return this.run(source.replace(/\r\n/g, '\n'), true) } -} \ No newline at end of file +} diff --git a/packages/lexer/src/index.ts b/packages/lexer/src/index.ts index 27faa37..14caac0 100644 --- a/packages/lexer/src/index.ts +++ b/packages/lexer/src/index.ts @@ -1,15 +1,13 @@ import { + Lexer, + parseRule, + getString, StringLike, + TokenLike, LexerMacros, LexerConfig, LexerRule, - LexerInstance, LexerRegexRule, - InlineLexerInstance, - TokenLike, - MatchStatus, - parseRule, - getString, } from '@marklet/core' export interface LexerOptions { @@ -25,29 +23,26 @@ export interface LexerOptions { config?: LexerConfig } -type NativeLexerContext = LexerRegexRule[] | InlineLexerInstance -export type LexerContexts = Record[] | InlineLexerInstance> - -interface LexerResult { - index: number - result: TokenLike[] -} +type InlineLexer = Lexer +type DocumentLexerRule = LexerRegexRule +type NativeLexerContext = DocumentLexerRule[] | InlineLexer +export type LexerContexts = Record[] | InlineLexer> -export class Lexer implements LexerInstance { - config: LexerConfig - private contexts: Record = {} +export class DocumentLexer extends Lexer { + private stackTrace: string[] + private contexts: Record = {} private entrance: string private inlineEntrance: string private requireBound: boolean constructor(contexts: LexerContexts, options: LexerOptions = {}) { - this.config = options.config || {} + super(options.config) this.entrance = options.entrance || 'main' this.inlineEntrance = options.inlineEntrance || 'text' this.requireBound = !!options.requireBound const _macros = options.macros || {} - const macros: Record = {} + const macros: LexerMacros = {} for (const key in _macros) { macros[key] = getString(_macros[key]) } @@ -59,14 +54,25 @@ export class Lexer implements LexerInstance { } } - private getContext(context: string | InlineLexerInstance | LexerRule[], strictMode?: boolean) { + getContext( + context: string | InlineLexer | LexerRule[], + strictMode: boolean = false, + pushStack: boolean = true, + ) { + const name = typeof context === 'string' ? context : 'anonymous' + if (pushStack) { + this.stackTrace.push(name) + } else { + this.stackTrace[this.stackTrace.length - 1] = name + } + console.log('PUSH:', this.stackTrace) const result = typeof context === 'string' ? this.contexts[context] : context if (!result) throw new Error(`Context '${context}' was not found.`) if (result instanceof Array) { for (let i = result.length - 1; i >= 0; i -= 1) { const rule: LexerRule = result[i] if ('include' in rule) { - const includes = this.getContext(rule.include) + const includes = this.getContext(rule.include, false, false) if (includes instanceof Array) { result.splice(i, 1, ...includes) } else { @@ -88,116 +94,65 @@ export class Lexer implements LexerInstance { return result as NativeLexerContext } - private _parse(source: string, context: NativeLexerContext, isTopLevel?: boolean): LexerResult { - let index = 0, unmatch = '' - const result: TokenLike[] = [] - - // apply inline lexer + initialize(context: NativeLexerContext) { if (!(context instanceof Array)) { - const result = context.parse(source) + const result = context.run(this.meta.source) + this.stackTrace.pop() + console.log('POP:', this.stackTrace) return { index: result.index, - result: [result.output], + output: [result.output], } } + this.meta.output = [] + this.meta.context = context + } - while (source) { - let status: MatchStatus = MatchStatus.NO_MATCH - for (const rule of context) { - if (rule.top_level && !isTopLevel) continue - if (rule.context_begins && index) continue - - // test - let test = rule.test - if (typeof test === 'string') { - if (test.charAt(0) === '!') { - test = !this.config[test.slice(1)] - } else { - test = !!this.config[test] - } - } else if (typeof test === 'function') { - test = test.call(this, this.config) - } - if (!test) continue - - // regex - const capture = rule.regex.exec(source) - if (!capture) continue - source = source.slice(capture[0].length) - const start = index - index += capture[0].length - - // pop - const pop = rule.pop - status = pop ? MatchStatus.POP : MatchStatus.CONTINUE - - // push - let content: TokenLike[] = [], push = rule.push - if (push) { - const context = this.getContext(push, rule.strict) - const subtoken = this._parse(source, context) - content = subtoken.result.map((tok) => { - if (this.requireBound && typeof tok === 'object') { - tok.start += index - tok.end += index - } - return tok - }) - source = source.slice(subtoken.index) - index += subtoken.index - } - - // detect error - if (!pop && index === start) { - throw new Error(`Endless loop at '${ - source.slice(0, 10) - } ${ - source.length > 10 ? '...' : '' - }'.`) - } - - // resolve unmatch - if (unmatch) { - result.push(unmatch) - unmatch = '' - } + getContent(rule: DocumentLexerRule) { + const context = this.getContext(rule.push, rule.strict) + const result = this.run(this.meta.source, false, context) + const content = result.output.map((token) => { + if (this.requireBound && typeof token === 'object') { + token.start += this.meta.index + token.end += this.meta.index + } + return token + }) + this.stackTrace.pop() + console.log('POP:', this.stackTrace) + this.meta.source = this.meta.source.slice(result.index) + this.meta.index += result.index + return content + } - // token - let token = rule.token - if (typeof token === 'function') { - token = token.call(this, capture, content) - } else if (token === undefined) { - if (push) { - token = content - } else if (!pop) { - token = capture[0] - } - } - if (token instanceof Array) token = { content: token } - if (token) { - if (typeof token === 'object') { - token.type = token.type || rule.type - if (this.requireBound) { - token.start = start - token.end = index - } - } - result.push(token) - } + pushUnmatch() { + this.meta.output.push(this.meta.unmatch) + } - break + pushToken(rule: DocumentLexerRule, capture: RegExpExecArray, content: TokenLike[]) { + let token = rule.token + if (typeof token === 'function') { + token = token.call(this, capture, content) + } else if (token === undefined) { + if (rule.push) { + token = content + } else if (!rule.pop) { + token = capture[0] } - - if (status === MatchStatus.POP) break - if (status === MatchStatus.NO_MATCH) { - unmatch += source.charAt(0) - source = source.slice(1) - index += 1 + } + if (token instanceof Array) { + token = { content: token } + } + if (token) { + if (typeof token === 'object') { + token.type = token.type || rule.type + if (this.requireBound) { + token.start = this.meta.start + token.end = this.meta.index + } } + this.meta.output.push(token) } - - if (unmatch) result.push(unmatch) - return { index, result } } inline(source: string, context: string = this.inlineEntrance): string { @@ -205,12 +160,21 @@ export class Lexer implements LexerInstance { if (inlineContext instanceof Array) { throw new Error(`'${context}' is not a inline context.`) } - return inlineContext.parse(source).output + const result = inlineContext.run(source).output + this.stackTrace.pop() + console.log('POP:', this.stackTrace) + return result } parse(source: string, context: string = this.entrance): TokenLike[] { + this.stackTrace = [] const initialContext = this.getContext(context) source = source.replace(/\r\n/g, '\n') - return this._parse(source, initialContext, true).result + try { + return this.run(source, true, initialContext).output + } catch (error) { + console.log(this.stackTrace) + console.error(error) + } } } diff --git a/packages/parser/src/index.ts b/packages/parser/src/index.ts index 488a4ef..b66ec8e 100644 --- a/packages/parser/src/index.ts +++ b/packages/parser/src/index.ts @@ -1,6 +1,6 @@ import { LexerConfig, TokenLike } from '@marklet/core' import { InlineLexer } from '@marklet/inline' -import { Lexer } from '@marklet/lexer' +import { DocumentLexer } from '@marklet/lexer' function escape(html: string): string { return html @@ -101,7 +101,7 @@ class MarkletInlineLexer extends InlineLexer { } } -class MarkletLexer extends Lexer { +class MarkletLexer extends DocumentLexer { constructor(config: MarkletLexerConfig = {}) { super({ text: new MarkletInlineLexer(config), diff --git a/packages/syntax/src/index.ts b/packages/syntax/src/index.ts index 7ee1b73..0b62f7e 100644 --- a/packages/syntax/src/index.ts +++ b/packages/syntax/src/index.ts @@ -1,4 +1,4 @@ -import { Lexer, LexerContexts } from '@marklet/lexer' +import { DocumentLexer, LexerContexts } from '@marklet/lexer' type SyntaxRule = SyntaxMetaRule | SyntaxIncludeRule | SyntaxRegexRule interface SyntaxToken { scope: string, text: string } @@ -18,7 +18,7 @@ interface SyntaxRegexRule { token?: (capture: RegExpExecArray, content: SyntaxToken[]) => SyntaxToken | SyntaxToken[] } -export class SyntaxLexer extends Lexer { +export class SyntaxLexer extends DocumentLexer { constructor(contexts: Record, macros: Record = {}) { function traverse(context: SyntaxRule[]): void { let meta = '', firstRule = context[0] From 52b97b051b4dec596083937414da6bf806584d7b Mon Sep 17 00:00:00 2001 From: Shigma <1700011071@pku.edu.cn> Date: Sun, 30 Sep 2018 01:18:28 +0800 Subject: [PATCH 02/35] regression --- .../src/index.ts => core/src/document.ts} | 40 +-- packages/core/src/index.ts | 257 ++---------------- .../src/index.ts => core/src/inline.ts} | 6 +- packages/core/src/lexer.ts | 231 ++++++++++++++++ packages/inline/package.json | 26 -- packages/inline/tsconfig.json | 13 - packages/lexer/package.json | 26 -- packages/lexer/tsconfig.json | 13 - packages/parser/package.json | 4 +- packages/parser/src/index.ts | 4 +- packages/parser/tsconfig.json | 2 - packages/syntax/package.json | 2 +- packages/syntax/src/index.ts | 2 +- packages/syntax/tsconfig.json | 2 +- tsconfig.json | 2 - 15 files changed, 289 insertions(+), 341 deletions(-) rename packages/{lexer/src/index.ts => core/src/document.ts} (85%) rename packages/{inline/src/index.ts => core/src/inline.ts} (88%) create mode 100644 packages/core/src/lexer.ts delete mode 100644 packages/inline/package.json delete mode 100644 packages/inline/tsconfig.json delete mode 100644 packages/lexer/package.json delete mode 100644 packages/lexer/tsconfig.json diff --git a/packages/lexer/src/index.ts b/packages/core/src/document.ts similarity index 85% rename from packages/lexer/src/index.ts rename to packages/core/src/document.ts index 14caac0..46f6461 100644 --- a/packages/lexer/src/index.ts +++ b/packages/core/src/document.ts @@ -8,7 +8,7 @@ import { LexerConfig, LexerRule, LexerRegexRule, -} from '@marklet/core' +} from './lexer' export interface LexerOptions { /** lexer rule regex macros */ @@ -28,8 +28,20 @@ type DocumentLexerRule = LexerRegexRule type NativeLexerContext = DocumentLexerRule[] | InlineLexer export type LexerContexts = Record[] | InlineLexer> +enum ContextReason { + INCLUDE, + PUSH, + INLINE, + INITIAL, +} + +interface ContextLog { + name: string + reason: ContextReason +} + export class DocumentLexer extends Lexer { - private stackTrace: string[] + private stackTrace: ContextLog[] private contexts: Record = {} private entrance: string private inlineEntrance: string @@ -56,23 +68,24 @@ export class DocumentLexer extends Lexer { getContext( context: string | InlineLexer | LexerRule[], + reason: ContextReason, strictMode: boolean = false, - pushStack: boolean = true, ) { const name = typeof context === 'string' ? context : 'anonymous' - if (pushStack) { - this.stackTrace.push(name) + if (reason === ContextReason.INITIAL) { + this.stackTrace = [{ name, reason }] + } else if (reason !== ContextReason.INCLUDE) { + this.stackTrace.push({ name, reason }) } else { - this.stackTrace[this.stackTrace.length - 1] = name + this.stackTrace[this.stackTrace.length - 1].name = name } - console.log('PUSH:', this.stackTrace) const result = typeof context === 'string' ? this.contexts[context] : context if (!result) throw new Error(`Context '${context}' was not found.`) if (result instanceof Array) { for (let i = result.length - 1; i >= 0; i -= 1) { const rule: LexerRule = result[i] if ('include' in rule) { - const includes = this.getContext(rule.include, false, false) + const includes = this.getContext(rule.include, ContextReason.INCLUDE) if (includes instanceof Array) { result.splice(i, 1, ...includes) } else { @@ -97,8 +110,6 @@ export class DocumentLexer extends Lexer { initialize(context: NativeLexerContext) { if (!(context instanceof Array)) { const result = context.run(this.meta.source) - this.stackTrace.pop() - console.log('POP:', this.stackTrace) return { index: result.index, output: [result.output], @@ -109,7 +120,7 @@ export class DocumentLexer extends Lexer { } getContent(rule: DocumentLexerRule) { - const context = this.getContext(rule.push, rule.strict) + const context = this.getContext(rule.push, ContextReason.PUSH, rule.strict) const result = this.run(this.meta.source, false, context) const content = result.output.map((token) => { if (this.requireBound && typeof token === 'object') { @@ -119,7 +130,6 @@ export class DocumentLexer extends Lexer { return token }) this.stackTrace.pop() - console.log('POP:', this.stackTrace) this.meta.source = this.meta.source.slice(result.index) this.meta.index += result.index return content @@ -156,19 +166,17 @@ export class DocumentLexer extends Lexer { } inline(source: string, context: string = this.inlineEntrance): string { - const inlineContext = this.getContext(context) + const inlineContext = this.getContext(context, ContextReason.INLINE) if (inlineContext instanceof Array) { throw new Error(`'${context}' is not a inline context.`) } const result = inlineContext.run(source).output this.stackTrace.pop() - console.log('POP:', this.stackTrace) return result } parse(source: string, context: string = this.entrance): TokenLike[] { - this.stackTrace = [] - const initialContext = this.getContext(context) + const initialContext = this.getContext(context, ContextReason.INITIAL) source = source.replace(/\r\n/g, '\n') try { return this.run(source, true, initialContext).output diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 84b9b42..805e2de 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,231 +1,26 @@ -export type StringLike = string | RegExp - -export type LexerConfig = Record -export type LexerMacros = Record - -export type TokenLike = string | LexerToken -export interface LexerToken { - type?: string - text?: string - content?: TokenLike[] - start?: number - end?: number - [key: string]: any -} - -export type LexerRule< - S extends StringLike = RegExp, - T extends Lexer = Lexer, - R extends RegExpExecArray = RegExpExecArray, -> = LexerIncludeRule | LexerRegexRule - -export interface LexerIncludeRule { include: string } -export interface LexerRegexRule< - S extends StringLike = RegExp, - T extends Lexer = Lexer, - R extends RegExpExecArray = RegExpExecArray, -> { - /** the regular expression to execute */ - regex?: S - /** - * a string containing all the rule flags: - * - `b`: match when the context begins - * - `e`: match end of line - * - `i`: ignore case - * - `p`: pop from the current context - * - `s`: pop when no match is found - * - `t`: match top level context - */ - flags?: string - /** default type of the token */ - type?: string - /** whether the rule is to be executed */ - test?: string | boolean | ((this: T, config: LexerConfig) => boolean) - /** a result token */ - token?: TokenLike | TokenLike[] | (( - this: T, capture: R, content: TokenLike[] - ) => TokenLike | TokenLike[]) - /** the inner context */ - push?: string | LexerRule[] - /** pop from the current context */ - pop?: boolean - /** pop when no match is found */ - strict?: boolean - /** match when the context begins */ - context_begins?: boolean - /** match top level context */ - top_level?: boolean - /** whether to ignore case */ - ignore_case?: boolean - /** match end of line */ - eol?: boolean -} - -/** transform a string-like object into a raw string */ -export function getString(string: StringLike): string { - return string instanceof RegExp ? string.source : string -} - -/** transform lexer rules with string into ones with regexp */ -export function parseRule(rule: LexerRule, macros: LexerMacros = {}): LexerRule { - if (!('include' in rule)) { - if (rule.regex === undefined) { - rule.regex = /(?=[\s\S])/ - if (!rule.type) rule.type = 'default' - } - if (rule.test === undefined) rule.test = true - let src = getString(rule.regex) - let flags = '' - for (const key in macros) { - src = src.replace(new RegExp(`{{${key}}}`, 'g'), `(?:${macros[key]})`) - } - rule.flags = rule.flags || '' - if (rule.flags.replace(/[biepst]/g, '')) { - throw new Error(`'${rule.flags}' contains invalid rule flags.`) - } - if (rule.flags.includes('p')) rule.pop = true - if (rule.flags.includes('s')) rule.strict = true - if (rule.flags.includes('b')) rule.context_begins = true - if (rule.flags.includes('t')) rule.top_level = true - if (rule.flags.includes('e') || rule.eol) src += ' *(?:\\n+|$)' - if (rule.flags.includes('i') || rule.ignore_case) flags += 'i' - rule.regex = new RegExp('^(?:' + src + ')', flags) - if (rule.push instanceof Array) rule.push.forEach(_rule => parseRule(_rule, macros)) - } - return rule as LexerRule -} - -export enum MatchStatus { - /** No match was found */ - NO_MATCH, - /** Found match and continue */ - CONTINUE, - /** Found match and pop */ - POP, -} - -export interface LexerResult { - /** current index of the source string */ - index: number - /** output string or array */ - output: R -} - -export interface LexerMeta extends Partial> { - /** record where the match starts */ - start?: number - /** a copy of source string */ - source?: string - /** a string collecting unmatch chars */ - unmatch?: string - /** whether running at top level */ - isTopLevel?: boolean - /** current lexing context */ - context?: LexerRegexRule[] -} - -export abstract class Lexer { - meta: LexerMeta - config: LexerConfig - - constructor(config: LexerConfig) { - this.config = config || {} - } - - initialize?(...args: any[]): void | LexerResult - getCapture?(rule: LexerRegexRule, capture: RegExpExecArray): RegExpExecArray - getContent?(rule: LexerRegexRule): TokenLike[] - pushToken?(rule: LexerRegexRule, capture: RegExpExecArray, content: TokenLike[]): void - pushUnmatch?(): void - - run(source: string, isTopLevel?: boolean, ...args: any[]): LexerResult { - // store meta data from lower level - const _meta = this.meta - this.meta = { - source, - isTopLevel, - index: 0, - unmatch: '', - } - - // initialize or simply get the result - const final = this.initialize(...args) - if (final) return final - - // walk through the source string - while (this.meta.source) { - let status: MatchStatus = MatchStatus.NO_MATCH - for (const rule of this.meta.context) { - // Step 1: test before matching - if (rule.top_level && !this.meta.isTopLevel) continue - if (rule.context_begins && this.meta.index) continue - - let test = rule.test - if (typeof test === 'string') { - if (test.charAt(0) === '!') { - test = !this.config[test.slice(1)] - } else { - test = !!this.config[test] - } - } else if (typeof test === 'function') { - test = !!test.call(this, this.config) - } - if (!test) continue - - // Step 2: exec regex and get capture - const match = rule.regex.exec(this.meta.source) - if (!match) continue - this.meta.source = this.meta.source.slice(match[0].length) - this.meta.start = this.meta.index - this.meta.index += match[0].length - const capture = this.getCapture ? this.getCapture(rule, match) : match - - // Step 3: reset match status - status = rule.pop ? MatchStatus.POP : MatchStatus.CONTINUE - - // Step 4: get inner tokens - const content = rule.push && this.getContent ? this.getContent(rule) : [] - - // Step 5: detect endless loop - if (!rule.pop && this.meta.start === this.meta.index) { - throw new Error(`Endless loop at '${ - this.meta.source.slice(0, 10) - } ${ - this.meta.source.length > 10 ? '...' : '' - }'.`) - } - - // Step 6: handle unmatched chars - if (this.pushUnmatch && this.meta.unmatch) { - this.pushUnmatch() - this.meta.unmatch = '' - } - - // Step 7: push generated token - this.pushToken(rule, capture, content) - - // Step 8: break loop - break - } - - if (status === MatchStatus.POP) break - if (status === MatchStatus.NO_MATCH) { - this.meta.unmatch += this.meta.source.charAt(0) - this.meta.source = this.meta.source.slice(1) - this.meta.index += 1 - } - } - - // handle ramaining unmatched chars - if (this.pushUnmatch && this.meta.unmatch) this.pushUnmatch() - - const result: LexerResult = { - index: this.meta.index, - output: this.meta.output, - } - - // restore meta data for lower level - this.meta = _meta - return result - } -} +export { + Lexer, + parseRule, + getString, + TokenLike, + StringLike, + LexerRule, + LexerMeta, + LexerToken, + LexerConfig, + LexerMacros, + LexerResult, + LexerRegexRule, + LexerIncludeRule, +} from './lexer' + +export { + LexerOptions, + LexerContexts, + DocumentLexer, +} from './document' + +export { + InlineContext, + InlineLexer, +} from './inline' diff --git a/packages/inline/src/index.ts b/packages/core/src/inline.ts similarity index 88% rename from packages/inline/src/index.ts rename to packages/core/src/inline.ts index f49c762..5f9ee4e 100644 --- a/packages/inline/src/index.ts +++ b/packages/core/src/inline.ts @@ -5,10 +5,10 @@ import { LexerResult, LexerConfig, LexerRegexRule, -} from '@marklet/core' +} from './lexer' type InlineLexerRule = LexerRegexRule -export type InlineLexerContext = LexerRegexRule[] +export type InlineContext = LexerRegexRule[] class InlineCapture extends Array implements RegExpExecArray { index: number @@ -31,7 +31,7 @@ class InlineCapture extends Array implements RegExpExecArray { export class InlineLexer extends Lexer { private rules: InlineLexerRule[] - constructor(context: InlineLexerContext, config: LexerConfig) { + constructor(context: InlineContext, config: LexerConfig) { super(config) this.rules = context.map(rule => parseRule(rule) as InlineLexerRule) } diff --git a/packages/core/src/lexer.ts b/packages/core/src/lexer.ts new file mode 100644 index 0000000..8aa28a4 --- /dev/null +++ b/packages/core/src/lexer.ts @@ -0,0 +1,231 @@ +export type StringLike = string | RegExp + +export type LexerConfig = Record +export type LexerMacros = Record + +export type TokenLike = string | LexerToken +export interface LexerToken { + type?: string + text?: string + content?: TokenLike[] + start?: number + end?: number + [key: string]: any +} + +export type LexerRule< + S extends StringLike = RegExp, + T extends Lexer = Lexer, + R extends RegExpExecArray = RegExpExecArray, +> = LexerIncludeRule | LexerRegexRule + +export interface LexerIncludeRule { include: string } +export interface LexerRegexRule< + S extends StringLike = RegExp, + T extends Lexer = Lexer, + R extends RegExpExecArray = RegExpExecArray, +> { + /** the regular expression to execute */ + regex?: S + /** + * a string containing all the rule flags: + * - `b`: match when the context begins + * - `e`: match end of line + * - `i`: ignore case + * - `p`: pop from the current context + * - `s`: pop when no match is found + * - `t`: match top level context + */ + flags?: string + /** default type of the token */ + type?: string + /** whether the rule is to be executed */ + test?: string | boolean | ((this: T, config: LexerConfig) => boolean) + /** a result token */ + token?: TokenLike | TokenLike[] | (( + this: T, capture: R, content: TokenLike[] + ) => TokenLike | TokenLike[]) + /** the inner context */ + push?: string | LexerRule[] + /** pop from the current context */ + pop?: boolean + /** pop when no match is found */ + strict?: boolean + /** match when the context begins */ + context_begins?: boolean + /** match top level context */ + top_level?: boolean + /** whether to ignore case */ + ignore_case?: boolean + /** match end of line */ + eol?: boolean +} + +/** transform a string-like object into a raw string */ +export function getString(string: StringLike): string { + return string instanceof RegExp ? string.source : string +} + +/** transform lexer rules with string into ones with regexp */ +export function parseRule(rule: LexerRule, macros: LexerMacros = {}): LexerRule { + if (!('include' in rule)) { + if (rule.regex === undefined) { + rule.regex = /(?=[\s\S])/ + if (!rule.type) rule.type = 'default' + } + if (rule.test === undefined) rule.test = true + let src = getString(rule.regex) + let flags = '' + for (const key in macros) { + src = src.replace(new RegExp(`{{${key}}}`, 'g'), `(?:${macros[key]})`) + } + rule.flags = rule.flags || '' + if (rule.flags.replace(/[biepst]/g, '')) { + throw new Error(`'${rule.flags}' contains invalid rule flags.`) + } + if (rule.flags.includes('p')) rule.pop = true + if (rule.flags.includes('s')) rule.strict = true + if (rule.flags.includes('b')) rule.context_begins = true + if (rule.flags.includes('t')) rule.top_level = true + if (rule.flags.includes('e') || rule.eol) src += ' *(?:\\n+|$)' + if (rule.flags.includes('i') || rule.ignore_case) flags += 'i' + rule.regex = new RegExp('^(?:' + src + ')', flags) + if (rule.push instanceof Array) rule.push.forEach(_rule => parseRule(_rule, macros)) + } + return rule as LexerRule +} + +enum MatchStatus { + /** No match was found */ + NO_MATCH, + /** Found match and continue */ + CONTINUE, + /** Found match and pop */ + POP, +} + +export interface LexerResult { + /** current index of the source string */ + index: number + /** output string or array */ + output: R +} + +export interface LexerMeta extends Partial> { + /** record where the match starts */ + start?: number + /** a copy of source string */ + source?: string + /** a string collecting unmatch chars */ + unmatch?: string + /** whether running at top level */ + isTopLevel?: boolean + /** current lexing context */ + context?: LexerRegexRule[] +} + +export abstract class Lexer { + meta: LexerMeta + config: LexerConfig + + constructor(config: LexerConfig) { + this.config = config || {} + } + + initialize?(...args: any[]): void | LexerResult + getCapture?(rule: LexerRegexRule, capture: RegExpExecArray): RegExpExecArray + getContent?(rule: LexerRegexRule): TokenLike[] + pushToken?(rule: LexerRegexRule, capture: RegExpExecArray, content: TokenLike[]): void + pushUnmatch?(): void + + run(source: string, isTopLevel?: boolean, ...args: any[]): LexerResult { + // store meta data from lower level + const _meta = this.meta + this.meta = { + source, + isTopLevel, + index: 0, + unmatch: '', + } + + // initialize or simply get the result + const final = this.initialize(...args) + if (final) return final + + // walk through the source string + while (this.meta.source) { + let status: MatchStatus = MatchStatus.NO_MATCH + for (const rule of this.meta.context) { + // Step 1: test before matching + if (rule.top_level && !this.meta.isTopLevel) continue + if (rule.context_begins && this.meta.index) continue + + let test = rule.test + if (typeof test === 'string') { + if (test.charAt(0) === '!') { + test = !this.config[test.slice(1)] + } else { + test = !!this.config[test] + } + } else if (typeof test === 'function') { + test = !!test.call(this, this.config) + } + if (!test) continue + + // Step 2: exec regex and get capture + const match = rule.regex.exec(this.meta.source) + if (!match) continue + this.meta.source = this.meta.source.slice(match[0].length) + this.meta.start = this.meta.index + this.meta.index += match[0].length + const capture = this.getCapture ? this.getCapture(rule, match) : match + + // Step 3: reset match status + status = rule.pop ? MatchStatus.POP : MatchStatus.CONTINUE + + // Step 4: get inner tokens + const content = rule.push && this.getContent ? this.getContent(rule) : [] + + // Step 5: detect endless loop + if (!rule.pop && this.meta.start === this.meta.index) { + throw new Error(`Endless loop at '${ + this.meta.source.slice(0, 10) + } ${ + this.meta.source.length > 10 ? '...' : '' + }'.`) + } + + // Step 6: handle unmatched chars + if (this.pushUnmatch && this.meta.unmatch) { + this.pushUnmatch() + this.meta.unmatch = '' + } + + // Step 7: push generated token + this.pushToken(rule, capture, content) + + // Step 8: break loop + break + } + + if (status === MatchStatus.POP) break + if (status === MatchStatus.NO_MATCH) { + this.meta.unmatch += this.meta.source.charAt(0) + this.meta.source = this.meta.source.slice(1) + this.meta.index += 1 + } + } + + // handle ramaining unmatched chars + if (this.pushUnmatch && this.meta.unmatch) this.pushUnmatch() + + const result: LexerResult = { + index: this.meta.index, + output: this.meta.output, + } + + // restore meta data for lower level + this.meta = _meta + return result + } +} diff --git a/packages/inline/package.json b/packages/inline/package.json deleted file mode 100644 index e6038e5..0000000 --- a/packages/inline/package.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "@marklet/inline", - "version": "1.0.0", - "description": "A fast inline lexer for marklet.", - "author": "shigma <1700011071@pku.edu.cn>", - "contributors": [ - "jjyyxx <1449843302@qq.com>" - ], - "homepage": "https://github.com/obstudio/Marklet", - "license": "MIT", - "main": "dist/index.js", - "typings": "dist/index.d.ts", - "files": [ - "dist" - ], - "repository": { - "type": "git", - "url": "git+https://github.com/obstudio/Marklet.git" - }, - "bugs": { - "url": "https://github.com/obstudio/Marklet/issues" - }, - "dependencies": { - "@marklet/core": "^2.0.0" - } -} diff --git a/packages/inline/tsconfig.json b/packages/inline/tsconfig.json deleted file mode 100644 index 9677f08..0000000 --- a/packages/inline/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "include": [ - "src" - ], - "compilerOptions": { - "outDir": "dist", - "rootDir": "src" - }, - "references": [ - { "path": "../core" } - ] -} \ No newline at end of file diff --git a/packages/lexer/package.json b/packages/lexer/package.json deleted file mode 100644 index 5cf1771..0000000 --- a/packages/lexer/package.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "@marklet/lexer", - "version": "1.0.10", - "description": "A document lexer for marklet.", - "author": "shigma <1700011071@pku.edu.cn>", - "contributors": [ - "jjyyxx <1449843302@qq.com>" - ], - "homepage": "https://github.com/obstudio/Marklet", - "license": "MIT", - "main": "dist/index.js", - "typings": "dist/index.d.ts", - "files": [ - "dist" - ], - "repository": { - "type": "git", - "url": "git+https://github.com/obstudio/Marklet.git" - }, - "bugs": { - "url": "https://github.com/obstudio/Marklet/issues" - }, - "dependencies": { - "@marklet/core": "^2.0.0" - } -} \ No newline at end of file diff --git a/packages/lexer/tsconfig.json b/packages/lexer/tsconfig.json deleted file mode 100644 index 9677f08..0000000 --- a/packages/lexer/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "include": [ - "src" - ], - "compilerOptions": { - "outDir": "dist", - "rootDir": "src" - }, - "references": [ - { "path": "../core" } - ] -} \ No newline at end of file diff --git a/packages/parser/package.json b/packages/parser/package.json index a6e8865..b0c0745 100644 --- a/packages/parser/package.json +++ b/packages/parser/package.json @@ -21,8 +21,6 @@ "url": "https://github.com/obstudio/Marklet/issues" }, "dependencies": { - "@marklet/core": "^2.0.0", - "@marklet/lexer": "^1.0.10", - "@marklet/inline": "^1.0.0" + "@marklet/core": "^2.0.0" } } \ No newline at end of file diff --git a/packages/parser/src/index.ts b/packages/parser/src/index.ts index b66ec8e..d8a581a 100644 --- a/packages/parser/src/index.ts +++ b/packages/parser/src/index.ts @@ -1,6 +1,4 @@ -import { LexerConfig, TokenLike } from '@marklet/core' -import { InlineLexer } from '@marklet/inline' -import { DocumentLexer } from '@marklet/lexer' +import { LexerConfig, TokenLike, DocumentLexer, InlineLexer } from '@marklet/core' function escape(html: string): string { return html diff --git a/packages/parser/tsconfig.json b/packages/parser/tsconfig.json index 8a214b6..4ad9fb3 100644 --- a/packages/parser/tsconfig.json +++ b/packages/parser/tsconfig.json @@ -9,7 +9,5 @@ }, "references": [ { "path": "../core" }, - { "path": "../inline" }, - { "path": "../lexer" } ] } \ No newline at end of file diff --git a/packages/syntax/package.json b/packages/syntax/package.json index 9000d29..99447e0 100644 --- a/packages/syntax/package.json +++ b/packages/syntax/package.json @@ -18,7 +18,7 @@ "url": "https://github.com/obstudio/Marklet/issues" }, "dependencies": { - "@marklet/lexer": "^1.0.10", + "@marklet/core": "^2.0.0", "js-yaml": "^3.12.0" } } \ No newline at end of file diff --git a/packages/syntax/src/index.ts b/packages/syntax/src/index.ts index 0b62f7e..a2661a5 100644 --- a/packages/syntax/src/index.ts +++ b/packages/syntax/src/index.ts @@ -1,4 +1,4 @@ -import { DocumentLexer, LexerContexts } from '@marklet/lexer' +import { DocumentLexer, LexerContexts } from '@marklet/core' type SyntaxRule = SyntaxMetaRule | SyntaxIncludeRule | SyntaxRegexRule interface SyntaxToken { scope: string, text: string } diff --git a/packages/syntax/tsconfig.json b/packages/syntax/tsconfig.json index 004deda..9677f08 100644 --- a/packages/syntax/tsconfig.json +++ b/packages/syntax/tsconfig.json @@ -8,6 +8,6 @@ "rootDir": "src" }, "references": [ - { "path": "../lexer" } + { "path": "../core" } ] } \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 6dc9dbf..ef85225 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,8 +3,6 @@ { "path": "./packages/core" }, { "path": "./packages/dev-server" }, { "path": "./packages/detok" }, - { "path": "./packages/inline" }, - { "path": "./packages/lexer" }, { "path": "./packages/parser" }, { "path": "./packages/syntax" } ], From 7fa501e9fd3e02a4fd22f651014d24b06a3a460a Mon Sep 17 00:00:00 2001 From: jjyyxx Date: Wed, 3 Oct 2018 10:54:42 +0800 Subject: [PATCH 03/35] fix --- packages/core/src/lexer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/lexer.ts b/packages/core/src/lexer.ts index 8aa28a4..2ea7f39 100644 --- a/packages/core/src/lexer.ts +++ b/packages/core/src/lexer.ts @@ -150,7 +150,7 @@ export abstract class Lexer { // initialize or simply get the result const final = this.initialize(...args) - if (final) return final + if (final) return this.meta = _meta, final // walk through the source string while (this.meta.source) { From 4020a238bbd4249a3ed60213b8fcb81d41e80b98 Mon Sep 17 00:00:00 2001 From: Shigma <1700011071@pku.edu.cn> Date: Wed, 3 Oct 2018 17:21:05 +0800 Subject: [PATCH 04/35] add some adjustments --- build/clear.js | 11 -- package.json | 2 - packages/cli/package.json | 6 +- packages/core/package.json | 2 +- packages/detok/package.json | 4 +- packages/dev-server/package.json | 6 +- packages/dev-server/src/client.ts | 4 +- packages/marklet/index.d.ts | 4 +- packages/marklet/index.js | 6 +- packages/marklet/package.json | 8 +- packages/parser/package.json | 4 +- packages/parser/src/document.ts | 187 ++++++++++++++++++++ packages/parser/src/index.ts | 280 +----------------------------- packages/parser/src/inline.ts | 88 ++++++++++ packages/renderer/package.json | 4 +- packages/syntax/package.json | 4 +- packages/test/package.json | 6 +- 17 files changed, 310 insertions(+), 316 deletions(-) delete mode 100644 build/clear.js create mode 100644 packages/parser/src/document.ts create mode 100644 packages/parser/src/inline.ts diff --git a/build/clear.js b/build/clear.js deleted file mode 100644 index 00c84c6..0000000 --- a/build/clear.js +++ /dev/null @@ -1,11 +0,0 @@ -const fs = require('fs') -const path = require('path') - -const BASE_DIR = path.join(__dirname, '../packages') - -fs.readdirSync(BASE_DIR).forEach((dir) => { - fs.readdirSync(path.join(BASE_DIR, dir)) - .forEach((file) => { - if (/[\w.-]+\.tgz/.test(file)) fs.unlinkSync(path.join(BASE_DIR, dir, file)) - }) -}) diff --git a/package.json b/package.json index c731339..c09a926 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,12 @@ { "scripts": { "start": "npm run build && node packages/cli -m edit", - "publish": "lerna publish --no-git-tag-version", "bootstrap": "lerna bootstrap --hoist --no-ci", "build": "tsc -b && node build/build -sr", "build:prod": "tsc -b && node build/build -psr", "build:renderer": "node build/build -r", "build:server": "node build/build -s", "build:tsc": "tsc -b", - "clear": "node build/clear", "test": "node packages/test/runner.js", "lint": "tslint packages/**/src/*.ts && eslint ." }, diff --git a/packages/cli/package.json b/packages/cli/package.json index 0acb1ba..57dd263 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@marklet/cli", - "version": "1.1.5", + "version": "1.1.6", "description": "A command line interface for marklet.", "author": "jjyyxx <1449843302@qq.com>", "contributors": [ @@ -19,8 +19,8 @@ "url": "https://github.com/obstudio/Marklet/issues" }, "dependencies": { - "@marklet/dev-server": "^1.0.12", - "@marklet/parser": "^1.1.0", + "@marklet/dev-server": "^1.0.13", + "@marklet/parser": "^1.1.1", "chalk": "^2.4.1", "commander": "^2.18.0", "js-yaml": "^3.12.0" diff --git a/packages/core/package.json b/packages/core/package.json index c294c16..12898bd 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@marklet/core", - "version": "2.0.0", + "version": "2.1.0", "description": "Some core conceptions of marklet.", "author": "shigma <1700011071@pku.edu.cn>", "contributors": [ diff --git a/packages/detok/package.json b/packages/detok/package.json index dfdb3af..cc4a2ed 100644 --- a/packages/detok/package.json +++ b/packages/detok/package.json @@ -1,6 +1,6 @@ { "name": "@marklet/detok", - "version": "1.0.10", + "version": "1.0.11", "description": "A detokenizer for marklet.", "author": "jjyyxx <1449843302@qq.com>", "contributors": [ @@ -24,6 +24,6 @@ "cheerio": "^1.0.0-rc.2" }, "devDependencies": { - "@marklet/core": "^2.0.0" + "@marklet/core": "^2.1.0" } } \ No newline at end of file diff --git a/packages/dev-server/package.json b/packages/dev-server/package.json index 4b2c846..4f79f87 100644 --- a/packages/dev-server/package.json +++ b/packages/dev-server/package.json @@ -1,6 +1,6 @@ { "name": "@marklet/dev-server", - "version": "1.0.12", + "version": "1.0.13", "description": "A develop server for marklet.", "author": "jjyyxx <1449843302@qq.com>", "contributors": [ @@ -21,8 +21,8 @@ "url": "https://github.com/obstudio/Marklet/issues" }, "dependencies": { - "@marklet/parser": "^1.1.0", - "@marklet/renderer": "^1.1.2", + "@marklet/parser": "^1.1.1", + "@marklet/renderer": "^1.1.3", "vue": "^2.5.17", "ws": "^6.0.0" } diff --git a/packages/dev-server/src/client.ts b/packages/dev-server/src/client.ts index a0a006d..169cdd6 100644 --- a/packages/dev-server/src/client.ts +++ b/packages/dev-server/src/client.ts @@ -1,6 +1,6 @@ import Vue from 'vue' import * as renderer from '@marklet/renderer' -import { Lexer, LexerConfig } from '@marklet/parser' +import { DocumentLexer, LexerConfig } from '@marklet/parser' declare module 'vue/types/vue' { interface Vue { @@ -70,7 +70,7 @@ export const Marklet = { edit: require('@/edit.vue'), }, parse(source: string, config: LexerConfig) { - return new Lexer(config).parse(source) + return new DocumentLexer(config).parse(source) }, start({ el, type }: { el: string | HTMLElement, type: 'watch' | 'edit' }) { document.title = 'Marklet - ' + type diff --git a/packages/marklet/index.d.ts b/packages/marklet/index.d.ts index a1d61dd..6917436 100644 --- a/packages/marklet/index.d.ts +++ b/packages/marklet/index.d.ts @@ -1,5 +1,5 @@ -import { parse, Lexer, LexerConfig } from '@marklet/parser' +import { parse, DocumentLexer, LexerConfig } from '@marklet/parser' -export { parse, Lexer } +export { parse, DocumentLexer as Lexer } export let config: LexerConfig export function render(element: string | HTMLElement, source: string): void diff --git a/packages/marklet/index.js b/packages/marklet/index.js index 92823c8..8a6f7a0 100644 --- a/packages/marklet/index.js +++ b/packages/marklet/index.js @@ -1,7 +1,7 @@ -const { parse, Lexer } = require('@marklet/parser') +const { parse, DocumentLexer } = require('@marklet/parser') const renderer = require('@marklet/renderer') -const lexer = new Lexer() +const lexer = new DocumentLexer() const config = lexer.config function render(element, source) { @@ -9,8 +9,8 @@ function render(element, source) { } module.exports = { + Lexer: DocumentLexer, parse, - Lexer, config, render, } diff --git a/packages/marklet/package.json b/packages/marklet/package.json index e9262d3..55486e3 100644 --- a/packages/marklet/package.json +++ b/packages/marklet/package.json @@ -1,6 +1,6 @@ { "name": "markletjs", - "version": "1.1.13", + "version": "1.1.14", "description": "A markup language designed for API manual pages.", "author": "jjyyxx <1449843302@qq.com>", "contributors": [ @@ -28,8 +28,8 @@ "url": "https://github.com/obstudio/Marklet/issues" }, "dependencies": { - "@marklet/cli": "^1.1.5", - "@marklet/parser": "^1.1.0", - "@marklet/renderer": "^1.1.2" + "@marklet/cli": "^1.1.6", + "@marklet/parser": "^1.1.1", + "@marklet/renderer": "^1.1.3" } } \ No newline at end of file diff --git a/packages/parser/package.json b/packages/parser/package.json index b0c0745..13b9a3e 100644 --- a/packages/parser/package.json +++ b/packages/parser/package.json @@ -1,6 +1,6 @@ { "name": "@marklet/parser", - "version": "1.1.0", + "version": "1.1.1", "description": "A document lexer for marklet.", "author": "shigma <1700011071@pku.edu.cn>", "contributors": [ @@ -21,6 +21,6 @@ "url": "https://github.com/obstudio/Marklet/issues" }, "dependencies": { - "@marklet/core": "^2.0.0" + "@marklet/core": "^2.1.0" } } \ No newline at end of file diff --git a/packages/parser/src/document.ts b/packages/parser/src/document.ts new file mode 100644 index 0000000..3bcbd7a --- /dev/null +++ b/packages/parser/src/document.ts @@ -0,0 +1,187 @@ +import { DocumentLexer, TokenLike } from '@marklet/core' +import { LexerConfig } from './index' +import MarkletInlineLexer from './inline' + +function collect(content: TokenLike[]) { + return content +} + +export default class extends DocumentLexer { + constructor(config: LexerConfig = {}) { + super({ + text: new MarkletInlineLexer(config), + main: [ + { + type: 'newline', + regex: /\n+/, + token: null + }, + { + type: 'heading', + regex: /(#{1,4}) +([^\n]+?)( +#)?/, + eol: true, + token(cap) { + let text, center + if (this.config.header_align && cap[3]) { + text = this.inline(cap[2]) + center = true + } else { + text = this.inline(cap[2] + (cap[3] || '')) + center = false + } + return { level: cap[1].length, text, center } + } + }, + { + type: 'section', + test: 'allow_section', + regex: /(\^{1,4}) +([^\n]+?)/, + eol: true, + push: 'main', + token(cap) { + return { + level: cap[1].length, + text: this.inline(cap[2]), + } + } + }, + { + type: 'quote', + regex: />([\w-]*) +/, + push: 'block', + token: (cap, content) => ({ style: cap[1], content }) + }, + { + type: 'separator', + regex: / *([-=])(\1|\.\1| \1)\2+/, + eol: true, + token: (cap) => ({ + thick: cap[1] === '=', + style: cap[2].length === 1 ? 'normal' + : cap[2][0] === ' ' ? 'dashed' : 'dotted' + }) + }, + { + type: 'codeblock', + regex: / *(`{3,}) *([\w-]+)? *\n([\s\S]*?)\n? *\1/, + eol: true, + token(cap) { + return { + lang: cap[2] || this.config.default_language, + text: cap[3] || '', + } + } + }, + { + type: 'usages', + regex: /(?= *\? +\S)/, + strict: true, + push: [ + { + type: 'usage', + regex: / *\? +([^\n]+?)/, + eol: true, + push: [ + { + regex: /(?= *\? )/, + pop: true + }, + { + include: 'text' + } + ], + token(cap, cont) { + return { + text: this.inline(cap[1]), + content: cont, + } + } + } + ] + }, + { + type: 'list', + regex: / *(?={{bullet}} +[^\n]+)/, + strict: true, + push: [ + { + type: 'item', + regex: /( *)({{bullet}}) +(?=[^\n]+)/, + push: [{ + regex: /\n? *(?={{bullet}} +[^\n]+)/, + pop: true + }, { + include: 'text' + }], + token(cap, cont) { + return { + text: cont.join(''), + ordered: cap[2].length > 1, + indent: cap[1].length, + } + } + } + ], + token: (_, cont) => collect(cont) + }, + { + type: 'inlinelist', + regex: /(?=\+)/, + push: [ + { + type: 'item', + regex: /\+/, + push: [ + { + regex: /\+?$|\+\n(?=\+)|\+?(?=\n)|(?=\+)/, + pop: true + }, + { + include: 'text' + } + ], + token(_, cont) { + return cont.join('') + } + }, + { + regex: /\n|$/, + pop: true + } + ], + token: (_, cont) => ({ content: cont }) + }, + { + type: 'table', + regex: /$^/, // FIXME: placeholder for syntax discussion + push: [], + token: (_, cont) => ({ content: cont }) + }, + { + type: 'paragraph', + push: 'text', + token: (_, cont) => ({ text: cont.join('') }) + } + ], + block: [ + { + regex: /\n[ \t]*\n/, + pop: true + }, + { + include: 'main' + } + ], + }, { + macros: { + bullet: /-|\d+\./, + }, + config: { + header_align: true, + allow_section: true, + default_language: '', + ...config, + } + }) + } +} diff --git a/packages/parser/src/index.ts b/packages/parser/src/index.ts index d8a581a..f29188c 100644 --- a/packages/parser/src/index.ts +++ b/packages/parser/src/index.ts @@ -1,17 +1,6 @@ -import { LexerConfig, TokenLike, DocumentLexer, InlineLexer } from '@marklet/core' - -function escape(html: string): string { - return html - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"') - .replace(/'/g, ''') -} - -function collect(content: TokenLike[]) { - return content -} +import { LexerConfig, TokenLike } from '@marklet/core' +import MarkletInlineLexer from './inline' +import MarkletDocumentLexer from './document' interface MarkletLexerConfig extends LexerConfig { /** enable header to align at center */ @@ -22,263 +11,6 @@ interface MarkletLexerConfig extends LexerConfig { default_language?: string } -class MarkletInlineLexer extends InlineLexer { - constructor(config: MarkletLexerConfig = {}) { - super([ - { - regex: /(?=\n[ \t]*(\n|$))/, - pop: true - }, - { - type: 'escape', - regex: /\\([\s\S])/, - token: (cap) => cap[1] - }, - { - type: 'newline', - regex: /\n/, - token: '
' - }, - { - type: 'code', - regex: /(`+)\s*([\s\S]*?[^`]?)\s*\1(?!`)/, - token: (cap) => `${escape(cap[2])}` - }, - { - type: 'strikeout', - regex: /-(?=\S)([\s\S]*?\S)-(?!-)/, - token: (cap) => `${cap.inner}` - }, - { - type: 'underline', - regex: /_(?=\S)([\s\S]*?\S)_(?!_)/, - token: (cap) => `${cap.inner}` - }, - { - type: 'bold', - regex: /\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/, - token: (cap) => `${cap.inner}` - }, - { - type: 'italic', - regex: /\*(?=\S)([\s\S]*?\S)\*(?!\*)/, - token: (cap) => `${cap.inner}` - }, - { - type: 'comment', - regex: /\(\((?=\S)([\s\S]*?\S)\)\)(?!\))/, - token: (cap) => `${cap.inner}` - }, - { - type: 'package', - regex: /{{(?=\S)([\s\S]*?\S)}}(?!})/, - token: (cap) => `${cap.inner}` - }, - { - type: 'link', - regex: /\[(?:([^\]|]+)\|)?([^\]]+)\]/, - token(cap) { - let text, match - if (cap[1]) { - text = cap[1] - } else if (match = cap[2].match(/^\$\w+(#\w+)$/)) { - text = match[1] - // } else if (this.resolve(cap[2]) in this.options.dictionary) { // FIXME: function not added yet - // text = this.options.dictionary[this.resolve(cap[2])] - } else if (cap[2].includes('#') || cap[2].includes('/')) { - text = cap[2].match(/[#/]([^#/]+)$/)[1] - } else { - text = cap[2] - } - return cap[2][0] === '!' ? - `${text}` : // TODO: special treatment like necessary? - `${text}` - } - } - ], config) - } -} - -class MarkletLexer extends DocumentLexer { - constructor(config: MarkletLexerConfig = {}) { - super({ - text: new MarkletInlineLexer(config), - main: [ - { - type: 'newline', - regex: /\n+/, - token: null - }, - { - type: 'heading', - regex: /(#{1,4}) +([^\n]+?)( +#)?/, - eol: true, - token(cap) { - let text, center - if (this.config.header_align && cap[3]) { - text = this.inline(cap[2]) - center = true - } else { - text = this.inline(cap[2] + (cap[3] || '')) - center = false - } - return { level: cap[1].length, text, center } - } - }, - { - type: 'section', - test: 'allow_section', - regex: /(\^{1,4}) +([^\n]+?)/, - eol: true, - push: 'main', - token(cap) { - return { - level: cap[1].length, - text: this.inline(cap[2]), - } - } - }, - { - type: 'quote', - regex: />([\w-]*) +/, - push: 'block', - token: (cap, content) => ({ style: cap[1], content }) - }, - { - type: 'separator', - regex: / *([-=])(\1|\.\1| \1)\2+/, - eol: true, - token: (cap) => ({ - thick: cap[1] === '=', - style: cap[2].length === 1 ? 'normal' - : cap[2][0] === ' ' ? 'dashed' : 'dotted' - }) - }, - { - type: 'codeblock', - regex: / *(`{3,}) *([\w-]+)? *\n([\s\S]*?)\n? *\1/, - eol: true, - token(cap) { - return { - lang: cap[2] || this.config.default_language, - text: cap[3] || '', - } - } - }, - { - type: 'usages', - regex: /(?= *\? +\S)/, - strict: true, - push: [ - { - type: 'usage', - regex: / *\? +([^\n]+?)/, - eol: true, - push: [ - { - regex: /(?= *\? )/, - pop: true - }, - { - include: 'text' - } - ], - token(cap, cont) { - return { - text: this.inline(cap[1]), - content: cont, - } - } - } - ] - }, - { - type: 'list', - regex: / *(?={{bullet}} +[^\n]+)/, - strict: true, - push: [ - { - type: 'item', - regex: /( *)({{bullet}}) +(?=[^\n]+)/, - push: [{ - regex: /\n? *(?={{bullet}} +[^\n]+)/, - pop: true - }, { - include: 'text' - }], - token(cap, cont) { - return { - text: cont.join(''), - ordered: cap[2].length > 1, - indent: cap[1].length, - } - } - } - ], - token: (_, cont) => collect(cont) - }, - { - type: 'inlinelist', - regex: /(?=\+)/, - push: [ - { - type: 'item', - regex: /\+/, - push: [ - { - regex: /\+?$|\+\n(?=\+)|\+?(?=\n)|(?=\+)/, - pop: true - }, - { - include: 'text' - } - ], - token(_, cont) { - return cont.join('') - } - }, - { - regex: /\n|$/, - pop: true - } - ], - token: (_, cont) => ({ content: cont }) - }, - { - type: 'table', - regex: /$^/, // FIXME: placeholder for syntax discussion - push: [], - token: (_, cont) => ({ content: cont }) - }, - { - type: 'paragraph', - push: 'text', - token: (_, cont) => ({ text: cont.join('') }) - } - ], - block: [ - { - regex: /\n[ \t]*\n/, - pop: true - }, - { - include: 'main' - } - ], - }, { - macros: { - bullet: /-|\d+\./, - }, - config: { - header_align: true, - allow_section: true, - default_language: '', - ...config, - } - }) - } -} - export interface ParseOptions { input: string config?: MarkletLexerConfig @@ -291,11 +23,11 @@ export function parse(options: ParseOptions): TokenLike[] { } else { throw new Error("'input' option is required.") } - return new MarkletLexer(options.config).parse(source) + return new MarkletDocumentLexer(options.config).parse(source) } export { - MarkletLexer as Lexer, - MarkletInlineLexer as InlineLexer, MarkletLexerConfig as LexerConfig, + MarkletInlineLexer as InlineLexer, + MarkletDocumentLexer as DocumentLexer, } diff --git a/packages/parser/src/inline.ts b/packages/parser/src/inline.ts new file mode 100644 index 0000000..9e83193 --- /dev/null +++ b/packages/parser/src/inline.ts @@ -0,0 +1,88 @@ +import { InlineLexer } from '@marklet/core' +import { LexerConfig } from './index' + +function escape(html: string): string { + return html + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, ''') +} + +export default class extends InlineLexer { + constructor(config: LexerConfig = {}) { + super([ + { + regex: /(?=\n[ \t]*(\n|$))/, + pop: true + }, + { + type: 'escape', + regex: /\\([\s\S])/, + token: (cap) => cap[1] + }, + { + type: 'newline', + regex: /\n/, + token: '
' + }, + { + type: 'code', + regex: /(`+)\s*([\s\S]*?[^`]?)\s*\1(?!`)/, + token: (cap) => `${escape(cap[2])}` + }, + { + type: 'strikeout', + regex: /-(?=\S)([\s\S]*?\S)-(?!-)/, + token: (cap) => `${cap.inner}` + }, + { + type: 'underline', + regex: /_(?=\S)([\s\S]*?\S)_(?!_)/, + token: (cap) => `${cap.inner}` + }, + { + type: 'bold', + regex: /\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/, + token: (cap) => `${cap.inner}` + }, + { + type: 'italic', + regex: /\*(?=\S)([\s\S]*?\S)\*(?!\*)/, + token: (cap) => `${cap.inner}` + }, + { + type: 'comment', + regex: /\(\((?=\S)([\s\S]*?\S)\)\)(?!\))/, + token: (cap) => `${cap.inner}` + }, + { + type: 'package', + regex: /{{(?=\S)([\s\S]*?\S)}}(?!})/, + token: (cap) => `${cap.inner}` + }, + { + type: 'link', + regex: /\[(?:([^\]|]+)\|)?([^\]]+)\]/, + token(cap) { + let text, match + if (cap[1]) { + text = cap[1] + } else if (match = cap[2].match(/^\$\w+(#\w+)$/)) { + text = match[1] + // } else if (this.resolve(cap[2]) in this.options.dictionary) { // FIXME: function not added yet + // text = this.options.dictionary[this.resolve(cap[2])] + } else if (cap[2].includes('#') || cap[2].includes('/')) { + text = cap[2].match(/[#/]([^#/]+)$/)[1] + } else { + text = cap[2] + } + return cap[2][0] === '!' ? + `${text}` : // TODO: special treatment like necessary? + `${text}` + } + } + ], config) + } +} diff --git a/packages/renderer/package.json b/packages/renderer/package.json index 15538f3..1bd35f8 100644 --- a/packages/renderer/package.json +++ b/packages/renderer/package.json @@ -1,6 +1,6 @@ { "name": "@marklet/renderer", - "version": "1.1.2", + "version": "1.1.3", "description": "A html renderer for marklet.", "author": "jjyyxx <1449843302@qq.com>", "contributors": [ @@ -21,7 +21,7 @@ "url": "https://github.com/obstudio/Marklet/issues" }, "dependencies": { - "@marklet/core": "^2.0.0-beta.0" + "@marklet/core": "^2.1.0" }, "peerDependencies": { "vue": "^2.5.17" diff --git a/packages/syntax/package.json b/packages/syntax/package.json index 99447e0..f73e294 100644 --- a/packages/syntax/package.json +++ b/packages/syntax/package.json @@ -1,6 +1,6 @@ { "name": "@marklet/syntax", - "version": "1.0.10", + "version": "1.0.11", "description": "A common language lexer for marklet.", "author": "shigma <1700011071@pku.edu.cn>", "homepage": "https://github.com/obstudio/Marklet", @@ -18,7 +18,7 @@ "url": "https://github.com/obstudio/Marklet/issues" }, "dependencies": { - "@marklet/core": "^2.0.0", + "@marklet/core": "^2.1.0", "js-yaml": "^3.12.0" } } \ No newline at end of file diff --git a/packages/test/package.json b/packages/test/package.json index 8a6ecc3..bfda2f4 100644 --- a/packages/test/package.json +++ b/packages/test/package.json @@ -1,6 +1,6 @@ { "name": "@marklet/test", - "version": "1.0.10", + "version": "1.0.11", "private": true, "author": "jjyyxx <1449843302@qq.com>", "homepage": "https://github.com/obstudio/Marklet", @@ -16,7 +16,7 @@ "url": "https://github.com/obstudio/Marklet/issues" }, "dependencies": { - "@marklet/detok": "^1.0.4", - "markletjs": "^1.1.13" + "@marklet/detok": "^1.0.11", + "markletjs": "^1.1.14" } } \ No newline at end of file From 2281ec174a4e7a92ea8c186b2683f15551061ec3 Mon Sep 17 00:00:00 2001 From: Shigma <1700011071@pku.edu.cn> Date: Wed, 3 Oct 2018 18:21:14 +0800 Subject: [PATCH 05/35] initial commit --- .vscode/settings.json | 4 +++- build/app-tp.js | 36 +++++++++++++++++++++++++++++++++ build/util.js | 36 +++++++++++++++++++++++++++++++++ package.json | 3 ++- packages/app/comp/.eslintrc.yml | 3 +++ packages/app/comp/app.vue | 14 +++++++++++++ packages/app/index.dev.html | 14 +++++++++++++ packages/app/index.html | 12 +++++++++++ packages/app/main.dev.js | 30 +++++++++++++++++++++++++++ packages/app/main.js | 30 +++++++++++++++++++++++++++ packages/app/package.json | 22 ++++++++++++++++++++ 11 files changed, 202 insertions(+), 2 deletions(-) create mode 100644 build/app-tp.js create mode 100644 packages/app/comp/.eslintrc.yml create mode 100644 packages/app/comp/app.vue create mode 100644 packages/app/index.dev.html create mode 100644 packages/app/index.html create mode 100644 packages/app/main.dev.js create mode 100644 packages/app/main.js create mode 100644 packages/app/package.json diff --git a/.vscode/settings.json b/.vscode/settings.json index 42b0849..72f3e66 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,7 +8,9 @@ "**/package-lock.json": true, "package.json.lerna_backup": true, "packages/**/node_modules": true, - // "packages/**/tsconfig.json": true + "packages/**/tsconfig.json": true, + "packages/**/dist": true, + "packages/**/temp": true, }, "editor.wordSeparators": "`~!@#%^&*()-=+[{]}\\|;:'\",.<>/?", } \ No newline at end of file diff --git a/build/app-tp.js b/build/app-tp.js new file mode 100644 index 0000000..7a163a9 --- /dev/null +++ b/build/app-tp.js @@ -0,0 +1,36 @@ +const sass = require('sass') +const path = require('path') +const util = require('./util') +const sfc2js = require('sfc2js') + +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', + outDir: 'app/temp', + enterance: process.argv[0].endsWith('electron.exe') ? 'app.vue' : '', +}) + +console.log('Transpile Succeed.', util.finish()) diff --git a/build/util.js b/build/util.js index 9785f28..5f8256f 100644 --- a/build/util.js +++ b/build/util.js @@ -26,8 +26,44 @@ function resolve(...names) { return path.join(__dirname, '../packages', ...names) } +const timers = {} + +function start(label = '') { + if (!timers[label]) timers[label] = { total: 0 } + timers[label].start = Date.now() + return _getTime(label) +} + +function pause(label = '') { + timers[label].total += Date.now() - timers[label].start + timers[label].start = Date.now() + return _getTime(label) +} + +function finish(label = '') { + pause(label) + const result = _getTime(label) + timers[label].total = 0 + return `Finished in ${result.toFixed(1)}s.` +} + +function _getTime(label = '') { + return label in timers ? timers[label].total / 1000 : 0 +} + +function timing(label = '', callback) { + start(label) + const result = callback() + pause(label) + return result +} + module.exports = { exec, execSync, resolve, + start, + pause, + finish, + timing, } \ No newline at end of file diff --git a/package.json b/package.json index c09a926..791f153 100644 --- a/package.json +++ b/package.json @@ -22,13 +22,14 @@ "chalk": "^2.4.1", "cheerio": "^1.0.0-rc.2", "commander": "^2.18.0", + "electron": "^3.0.2", "eslint": "^5.6.0", "eslint-plugin-vue": "^5.0.0-beta.3", "fast-deep-equal": "^2.0.1", "html-minifier": "^3.5.20", "lerna": "^3.4.0", "node-sass": "^4.9.3", - "prettier": "^1.14.3", + "sass": "^1.14.1", "semver": "^5.5.1", "sfc2js": "^3.3.0", "tslint": "^5.11.0", diff --git a/packages/app/comp/.eslintrc.yml b/packages/app/comp/.eslintrc.yml new file mode 100644 index 0000000..c3d454b --- /dev/null +++ b/packages/app/comp/.eslintrc.yml @@ -0,0 +1,3 @@ +extends: + - plugin:vue/essential + \ No newline at end of file diff --git a/packages/app/comp/app.vue b/packages/app/comp/app.vue new file mode 100644 index 0000000..3818654 --- /dev/null +++ b/packages/app/comp/app.vue @@ -0,0 +1,14 @@ + + + + diff --git a/packages/app/index.dev.html b/packages/app/index.dev.html new file mode 100644 index 0000000..db52e01 --- /dev/null +++ b/packages/app/index.dev.html @@ -0,0 +1,14 @@ + + + + + Marklet + + + + + +
+ + + diff --git a/packages/app/index.html b/packages/app/index.html new file mode 100644 index 0000000..658f0b0 --- /dev/null +++ b/packages/app/index.html @@ -0,0 +1,12 @@ + + + + + Marklet + + + +
+ + + diff --git a/packages/app/main.dev.js b/packages/app/main.dev.js new file mode 100644 index 0000000..dc7346c --- /dev/null +++ b/packages/app/main.dev.js @@ -0,0 +1,30 @@ +const { app, BrowserWindow } = require('electron') + +let mainWindow + +function createMain() { + mainWindow = new BrowserWindow({ + width: 800, + height: 600, + center: true, + resizable: false, + useContentSize: true, + autoHideMenuBar: false, + }) + + mainWindow.loadFile('index.dev.html') + + mainWindow.on('closed', () => { + mainWindow = null + }) +} + +app.on('ready', createMain) + +app.on('window-all-closed', () => { + if (process.platform !== 'darwin') app.quit() +}) + +app.on('activate', () => { + if (mainWindow === null) createMain() +}) diff --git a/packages/app/main.js b/packages/app/main.js new file mode 100644 index 0000000..ae47149 --- /dev/null +++ b/packages/app/main.js @@ -0,0 +1,30 @@ +const { app, BrowserWindow } = require('electron') + +let mainWindow + +function createMain() { + mainWindow = new BrowserWindow({ + width: 800, + height: 600, + center: true, + resizable: false, + useContentSize: true, + autoHideMenuBar: false, + }) + + mainWindow.loadFile('index.html') + + mainWindow.on('closed', () => { + mainWindow = null + }) +} + +app.on('ready', createMain) + +app.on('window-all-closed', () => { + if (process.platform !== 'darwin') app.quit() +}) + +app.on('activate', () => { + if (mainWindow === null) createMain() +}) diff --git a/packages/app/package.json b/packages/app/package.json new file mode 100644 index 0000000..8a7a5b3 --- /dev/null +++ b/packages/app/package.json @@ -0,0 +1,22 @@ +{ + "name": "@marklet/app", + "version": "1.0.0", + "private": true, + "author": "shigma <1700011071@pku.edu.cn>", + "homepage": "https://github.com/obstudio/Marklet", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/obstudio/Marklet.git" + }, + "scripts": { + "test": "echo \"Error: run tests from root\" && exit 1" + }, + "bugs": { + "url": "https://github.com/obstudio/Marklet/issues" + }, + "dependencies": { + "@marklet/parser": "^1.1.1", + "vue": "^2.5.17" + } +} \ No newline at end of file From 2d90cfbc8eed7250f45d3ee6ef2b889ee2ec78cc Mon Sep 17 00:00:00 2001 From: Shigma <1700011071@pku.edu.cn> Date: Wed, 3 Oct 2018 20:15:33 +0800 Subject: [PATCH 06/35] transpile --- .gitignore | 4 +++- .vscode/launch.json | 7 +++++-- .vscode/settings.json | 2 +- build/app-tp.js => packages/app/build/transpile.js | 2 +- packages/app/comp/app.vue | 6 ++---- packages/app/comp/index.css | 0 packages/app/index.dev.html | 9 ++++++++- packages/app/{index.html => index.prod.html} | 0 packages/app/main.dev.js | 3 ++- packages/app/{main.js => main.prod.js} | 3 ++- packages/app/package.json | 1 + 11 files changed, 25 insertions(+), 12 deletions(-) rename build/app-tp.js => packages/app/build/transpile.js (94%) create mode 100644 packages/app/comp/index.css rename packages/app/{index.html => index.prod.html} (100%) rename packages/app/{main.js => main.prod.js} (84%) diff --git a/.gitignore b/.gitignore index dce4c90..4db6739 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,6 @@ test.* dist temp !test -*.tgz \ No newline at end of file +*.tgz +packages/app/main.js +packages/app/index.html \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index f1a290f..ffbf1c8 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -6,8 +6,11 @@ "request": "launch", "name": "Debug Main Process", "cwd": "${workspaceRoot}", - "runtimeExecutable": "npm", - "args": [ "start" ], + "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron", + "windows": { + "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd" + }, + "args" : ["packages/app/main.dev.js"] } ] } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 72f3e66..691d6e0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,9 +5,9 @@ "vue" ], "files.exclude": { + "**/node_modules": true, "**/package-lock.json": true, "package.json.lerna_backup": true, - "packages/**/node_modules": true, "packages/**/tsconfig.json": true, "packages/**/dist": true, "packages/**/temp": true, diff --git a/build/app-tp.js b/packages/app/build/transpile.js similarity index 94% rename from build/app-tp.js rename to packages/app/build/transpile.js index 7a163a9..79b649e 100644 --- a/build/app-tp.js +++ b/packages/app/build/transpile.js @@ -1,6 +1,6 @@ +const util = require('../../../build/util') const sass = require('sass') const path = require('path') -const util = require('./util') const sfc2js = require('sfc2js') util.start() diff --git a/packages/app/comp/app.vue b/packages/app/comp/app.vue index 3818654..2309e99 100644 --- a/packages/app/comp/app.vue +++ b/packages/app/comp/app.vue @@ -1,10 +1,8 @@ diff --git a/packages/app/comp/index.css b/packages/app/comp/index.css new file mode 100644 index 0000000..e69de29 diff --git a/packages/app/index.dev.html b/packages/app/index.dev.html index db52e01..cc180db 100644 --- a/packages/app/index.dev.html +++ b/packages/app/index.dev.html @@ -4,11 +4,18 @@ Marklet +
- + diff --git a/packages/app/index.html b/packages/app/index.prod.html similarity index 100% rename from packages/app/index.html rename to packages/app/index.prod.html diff --git a/packages/app/main.dev.js b/packages/app/main.dev.js index dc7346c..b3a43b6 100644 --- a/packages/app/main.dev.js +++ b/packages/app/main.dev.js @@ -1,4 +1,5 @@ const { app, BrowserWindow } = require('electron') +const path = require('path') let mainWindow @@ -12,7 +13,7 @@ function createMain() { autoHideMenuBar: false, }) - mainWindow.loadFile('index.dev.html') + mainWindow.loadFile(path.join(__dirname, 'index.dev.html')) mainWindow.on('closed', () => { mainWindow = null diff --git a/packages/app/main.js b/packages/app/main.prod.js similarity index 84% rename from packages/app/main.js rename to packages/app/main.prod.js index ae47149..05d3118 100644 --- a/packages/app/main.js +++ b/packages/app/main.prod.js @@ -1,4 +1,5 @@ const { app, BrowserWindow } = require('electron') +const path = require('path') let mainWindow @@ -12,7 +13,7 @@ function createMain() { autoHideMenuBar: false, }) - mainWindow.loadFile('index.html') + mainWindow.loadFile(path.join(__dirname, 'index.prod.html')) mainWindow.on('closed', () => { mainWindow = null diff --git a/packages/app/package.json b/packages/app/package.json index 8a7a5b3..3d7285a 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -2,6 +2,7 @@ "name": "@marklet/app", "version": "1.0.0", "private": true, + "main": "main.js", "author": "shigma <1700011071@pku.edu.cn>", "homepage": "https://github.com/obstudio/Marklet", "license": "MIT", From 467af7367cef4b3197d9e5594615f541571e9794 Mon Sep 17 00:00:00 2001 From: Shigma <1700011071@pku.edu.cn> Date: Wed, 3 Oct 2018 21:42:05 +0800 Subject: [PATCH 07/35] basic usage --- packages/app/comp/.eslintrc.yml | 4 ++- packages/app/comp/app.vue | 52 +++++++++++++++++++++++++++++++-- packages/app/comp/index.css | 3 ++ packages/app/package.json | 1 + 4 files changed, 57 insertions(+), 3 deletions(-) diff --git a/packages/app/comp/.eslintrc.yml b/packages/app/comp/.eslintrc.yml index c3d454b..98ddf5e 100644 --- a/packages/app/comp/.eslintrc.yml +++ b/packages/app/comp/.eslintrc.yml @@ -1,3 +1,5 @@ extends: - plugin:vue/essential - \ No newline at end of file + +globals: + Vue: true diff --git a/packages/app/comp/app.vue b/packages/app/comp/app.vue index 2309e99..d24f04d 100644 --- a/packages/app/comp/app.vue +++ b/packages/app/comp/app.vue @@ -1,12 +1,60 @@