mirror of
https://github.com/BoostIO/BoostNote-App.git
synced 2024-10-04 08:07:41 +03:00
remove dependency on external github package & exlude mangling of coffeescript node names
This commit is contained in:
parent
b35e12d508
commit
3c0b659a62
973
package-lock.json
generated
973
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -13,7 +13,7 @@
|
||||
"start": "electron app/index.js",
|
||||
"lint": "eslint src/**/*.ts{,x}",
|
||||
"format": "prettier --write \"src/**/*\"",
|
||||
"build": "rimraf compiled && env-cmd cross-env TS_NODE_PROJECT=\"tsconfig-webpack.json\" webpack --mode production",
|
||||
"build": "rimraf compiled && env-cmd cross-env TS_NODE_PROJECT=\"tsconfig-webpack.json\" webpack --mode production --config webpack.production.conf.ts",
|
||||
"meta": "node scripts/meta.js",
|
||||
"prepack": "rimraf dist && env-cmd npm run meta && electron-builder --dir",
|
||||
"pack": "rimraf dist && env-cmd npm run meta && electron-builder",
|
||||
@ -50,6 +50,7 @@
|
||||
"@types/react-dom": "^16.9.0",
|
||||
"@types/react-hot-loader": "^4.1.0",
|
||||
"@types/shortid": "0.0.29",
|
||||
"@types/terser-webpack-plugin": "^2.2.0",
|
||||
"@types/webpack": "^4.39.1",
|
||||
"@types/webpack-dev-server": "^3.1.7",
|
||||
"@types/webpack-env": "^1.14.0",
|
||||
@ -75,6 +76,7 @@
|
||||
"react-test-renderer": "^16.9.0",
|
||||
"rimraf": "^3.0.0",
|
||||
"style-loader": "^1.0.0",
|
||||
"terser-webpack-plugin": "^2.2.3",
|
||||
"ts-loader": "^6.2.0",
|
||||
"ts-node": "^8.4.1",
|
||||
"type-fest": "^0.7.1",
|
||||
@ -95,8 +97,8 @@
|
||||
"aws-amplify": "^1.2.4",
|
||||
"classcat": "^4.0.2",
|
||||
"codemirror": "^5.49.0",
|
||||
"coffeescript": "^2.4.1",
|
||||
"copy-webpack-plugin": "^5.0.4",
|
||||
"cson-parser": "git+https://github.com/ButteryCrumpet/cson-parser.git",
|
||||
"dotenv": "^8.2.0",
|
||||
"electron-log": "^4.0.0",
|
||||
"electron-updater": "^4.2.0",
|
||||
|
231
src/lib/cson-parser/index.ts
Normal file
231
src/lib/cson-parser/index.ts
Normal file
@ -0,0 +1,231 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Groupon, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of GROUPON nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
import { runInThisContext } from 'vm'
|
||||
import { nodes } from 'coffeescript'
|
||||
|
||||
function defaultReviver(_: string, value: any) {
|
||||
return value
|
||||
}
|
||||
|
||||
function getFunctionNameIE(fn: any) {
|
||||
return fn.toString().match(/^function\s*([^( ]+)/)[1]
|
||||
}
|
||||
|
||||
function nodeTypeString(csNode: any) {
|
||||
const ref = csNode.constructor.name
|
||||
return ref != null ? ref : getFunctionNameIE(csNode.constructor)
|
||||
}
|
||||
|
||||
function syntaxErrorMessage(csNode: any, msg: string) {
|
||||
const ref = csNode.locationData
|
||||
const lineIdx = ref.first_line
|
||||
const columnIdx = ref.first_column
|
||||
let line = ''
|
||||
let column = ''
|
||||
if (lineIdx != null) {
|
||||
line = lineIdx + 1
|
||||
}
|
||||
if (columnIdx != null) {
|
||||
column = columnIdx + 1
|
||||
}
|
||||
return `Syntax error on line ${line}, column ${column}: ${msg}`
|
||||
}
|
||||
|
||||
function parseStringLiteral(literal: string) {
|
||||
return runInThisContext(literal)
|
||||
}
|
||||
|
||||
function parseRegExpLiteral(literal: string) {
|
||||
return runInThisContext(literal)
|
||||
}
|
||||
|
||||
function transformKey(csNode: any) {
|
||||
const type = nodeTypeString(csNode)
|
||||
if (type !== 'Value') {
|
||||
throw new SyntaxError(syntaxErrorMessage(csNode, `${type} used as key`))
|
||||
}
|
||||
const value = csNode.base.value
|
||||
switch (value.charAt(0)) {
|
||||
case "'":
|
||||
case '"':
|
||||
return parseStringLiteral(value)
|
||||
default:
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
const nodeTransforms = {
|
||||
Block: function Block(node: any, transformNode: any) {
|
||||
const expressions = node.expressions
|
||||
if (!expressions || expressions.length !== 1) {
|
||||
throw new SyntaxError(
|
||||
syntaxErrorMessage(node, 'One top level value expected')
|
||||
)
|
||||
}
|
||||
return transformNode(expressions[0])
|
||||
},
|
||||
Value: function Value(node: any, transformNode: any) {
|
||||
return transformNode(node.base)
|
||||
},
|
||||
Bool: function Bool(node: any) {
|
||||
return node.val === 'true'
|
||||
},
|
||||
BooleanLiteral: function BooleanLiteral(node: any) {
|
||||
return node.value === 'true'
|
||||
},
|
||||
Null: function Null() {
|
||||
return null
|
||||
},
|
||||
NullLiteral: function NullLiteral() {
|
||||
return null
|
||||
},
|
||||
Literal: function Literal(node: any) {
|
||||
const value = node.value
|
||||
try {
|
||||
switch (value.charAt(0)) {
|
||||
case "'":
|
||||
case '"':
|
||||
return parseStringLiteral(value)
|
||||
case '/':
|
||||
return parseRegExpLiteral(value)
|
||||
default:
|
||||
return JSON.parse(value)
|
||||
}
|
||||
} catch (error) {
|
||||
throw new SyntaxError(syntaxErrorMessage(node, error.message))
|
||||
}
|
||||
},
|
||||
NumberLiteral: function NumberLiteral(node: any) {
|
||||
return Number(node.value)
|
||||
},
|
||||
StringLiteral: function StringLiteral(node: any) {
|
||||
return parseStringLiteral(node.value)
|
||||
},
|
||||
RegexLiteral: function RegexLiteral(node: any) {
|
||||
return parseRegExpLiteral(node.value)
|
||||
},
|
||||
Arr: function Arr(node: any, transformNode: any) {
|
||||
return node.objects.map(transformNode)
|
||||
},
|
||||
Obj: function Obj(node: any, transformNode: any, reviver: any) {
|
||||
return node.properties.reduce((outObject: any, property: any) => {
|
||||
const variable = property.variable
|
||||
let value = property.value
|
||||
if (!variable) {
|
||||
return outObject
|
||||
}
|
||||
const keyName = transformKey(variable)
|
||||
value = transformNode(value)
|
||||
outObject[keyName] = reviver.call(outObject, keyName, value)
|
||||
return outObject
|
||||
}, {})
|
||||
},
|
||||
Op: function Op(node: any, transformNode: any) {
|
||||
if (node.second != null) {
|
||||
const left = transformNode(node.first)
|
||||
const right = transformNode(node.second)
|
||||
switch (node.operator) {
|
||||
case '-':
|
||||
return left - right
|
||||
case '+':
|
||||
return left + right
|
||||
case '*':
|
||||
return left * right
|
||||
case '/':
|
||||
return left / right
|
||||
case '%':
|
||||
return left % right
|
||||
case '&':
|
||||
return left & right
|
||||
case '|':
|
||||
return left | right
|
||||
case '^':
|
||||
return left ^ right
|
||||
case '<<':
|
||||
return left << right
|
||||
case '>>>':
|
||||
return left >>> right
|
||||
case '>>':
|
||||
return left >> right
|
||||
default:
|
||||
throw new SyntaxError(
|
||||
syntaxErrorMessage(node, `Unknown binary operator ${node.operator}`)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
switch (node.operator) {
|
||||
case '-':
|
||||
return -transformNode(node.first)
|
||||
case '~':
|
||||
return ~transformNode(node.first)
|
||||
default:
|
||||
throw new SyntaxError(
|
||||
syntaxErrorMessage(node, `Unknown unary operator ${node.operator}`)
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
Parens: function Parens(node: any, transformNode: any) {
|
||||
const expressions = node.body.expressions
|
||||
if (!expressions || expressions.length !== 1) {
|
||||
throw new SyntaxError(
|
||||
syntaxErrorMessage(node, 'Parenthesis may only contain one expression')
|
||||
)
|
||||
}
|
||||
return transformNode(expressions[0])
|
||||
}
|
||||
}
|
||||
|
||||
export function parse(source: string) {
|
||||
const reviver = defaultReviver
|
||||
|
||||
function transformNode(csNode: any) {
|
||||
const type = nodeTypeString(csNode)
|
||||
const transform = nodeTransforms[type]
|
||||
if (!transform) {
|
||||
throw new SyntaxError(syntaxErrorMessage(csNode, `Unexpected ${type}`))
|
||||
}
|
||||
return transform(csNode, transformNode, reviver)
|
||||
}
|
||||
if (typeof reviver !== 'function') {
|
||||
throw new TypeError('reviver has to be a function')
|
||||
}
|
||||
const coffeeAst = nodes(source.toString())
|
||||
const parsed = transformNode(coffeeAst)
|
||||
if (reviver === defaultReviver) {
|
||||
return parsed
|
||||
}
|
||||
const contextObj = {}
|
||||
contextObj[''] = parsed
|
||||
return reviver.call(contextObj, '', parsed)
|
||||
}
|
@ -1,13 +1,19 @@
|
||||
import { readAsText } from './utils/files'
|
||||
import { parse } from 'cson-parser'
|
||||
import { parse } from './cson-parser'
|
||||
import ow from 'ow'
|
||||
|
||||
type ParseErrors = 'read_error' | 'parse_error' | 'not_markdown'
|
||||
type ParseErrors =
|
||||
| 'read_error'
|
||||
| 'cson_parse_error'
|
||||
| 'not_markdown'
|
||||
| 'schema_parse_error'
|
||||
|
||||
type ParsedNote = {
|
||||
content: string
|
||||
tags: string[]
|
||||
title: string
|
||||
}
|
||||
|
||||
type ConvertResult =
|
||||
| { err: true; data: ParseErrors }
|
||||
| { err: false; data: ParsedNote }
|
||||
@ -23,13 +29,13 @@ export const convertCSONFileToNote = async (
|
||||
|
||||
const parsed = await parseCSON(text)
|
||||
|
||||
if (parsed === 'parse_error') {
|
||||
if (parsed === 'cson_parse_error') {
|
||||
return { err: true, data: parsed }
|
||||
}
|
||||
|
||||
const validated = validateNoteSchema(parsed)
|
||||
|
||||
if (validated === 'parse_error') {
|
||||
if (validated === 'schema_parse_error') {
|
||||
return { err: true, data: validated }
|
||||
}
|
||||
|
||||
@ -47,14 +53,15 @@ const readFile = (file: File) => readAsText(file).catch(() => 'read_error')
|
||||
const parseCSON = (text: string) => {
|
||||
try {
|
||||
return parse(text)
|
||||
} catch {
|
||||
return 'parse_error'
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return 'cson_parse_error'
|
||||
}
|
||||
}
|
||||
|
||||
const validateNoteSchema = (
|
||||
obj: any
|
||||
): ParsedNote & { type: string } | 'parse_error' => {
|
||||
): ParsedNote & { type: string } | 'schema_parse_error' => {
|
||||
const validator = ow.object.partialShape({
|
||||
tags: ow.optional.array.ofType(ow.string),
|
||||
content: ow.string,
|
||||
@ -66,7 +73,6 @@ const validateNoteSchema = (
|
||||
ow(obj, validator)
|
||||
return obj
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
return 'parse_error'
|
||||
return 'schema_parse_error'
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,8 @@
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"esModuleInterop": true
|
||||
"esModuleInterop": true,
|
||||
"allowJs": true
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx", "./typings/**/*.d.ts"]
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"jsx": "react",
|
||||
"allowJs": false,
|
||||
"allowJs": true,
|
||||
"checkJs": false,
|
||||
"lib": ["dom", "es2015", "es2016", "es2017", "esnext"],
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
|
3
typings/coffeescript.d.ts
vendored
Normal file
3
typings/coffeescript.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
declare module 'coffeescript' {
|
||||
export function nodes(input: any): any
|
||||
}
|
92
webpack.production.config.ts
Normal file
92
webpack.production.config.ts
Normal file
@ -0,0 +1,92 @@
|
||||
import path from 'path'
|
||||
import webpack from 'webpack'
|
||||
import HtmlWebpackPlugin from 'html-webpack-plugin'
|
||||
import ErrorOverlayPlugin from 'error-overlay-webpack-plugin'
|
||||
import CopyPlugin from 'copy-webpack-plugin'
|
||||
import TerserPlugin from 'terser-webpack-plugin'
|
||||
|
||||
module.exports = {
|
||||
mode: 'production',
|
||||
entry: [
|
||||
'./src/index.tsx'
|
||||
// the entry point of our app
|
||||
],
|
||||
|
||||
output: {
|
||||
filename: 'bundle.js',
|
||||
// the output bundle
|
||||
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
|
||||
publicPath: '/app'
|
||||
// necessary for HMR to know where to load the hot update chunks
|
||||
},
|
||||
|
||||
optimization: {
|
||||
minimize: true,
|
||||
minimizer: [
|
||||
new TerserPlugin({
|
||||
terserOptions: {
|
||||
keep_fnames: /Block|Value|Bool|BooleanLiteral|Null|NullLiteral|Literal|NumberLiteral|StringLiteral|RegexLiteral|Arr|Obj|Op|Parens/
|
||||
}
|
||||
})
|
||||
]
|
||||
},
|
||||
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.css$/i,
|
||||
use: ['style-loader', 'css-loader']
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpe?g|gif|svg|eot|ttf|woff|woff2)$/i,
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
limit: 8192
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
use: [{ loader: 'ts-loader' }],
|
||||
exclude: /node_modules/
|
||||
},
|
||||
{
|
||||
test: /\.md$/,
|
||||
use: [
|
||||
{
|
||||
loader: 'raw-loader'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
plugins: [
|
||||
new webpack.NoEmitOnErrorsPlugin(),
|
||||
// do not emit compiled assets that include errors
|
||||
new HtmlWebpackPlugin(),
|
||||
new ErrorOverlayPlugin(),
|
||||
new webpack.EnvironmentPlugin([
|
||||
'NODE_ENV',
|
||||
'AMPLIFY_AUTH_IDENTITY_POOL_ID',
|
||||
'AMPLIFY_AUTH_REGION',
|
||||
'AMPLIFY_PINPOINT_APPID',
|
||||
'AMPLIFY_PINPOINT_REGION',
|
||||
'BOOST_NOTE_BASE_URL'
|
||||
]),
|
||||
new CopyPlugin([
|
||||
{
|
||||
from: path.join(__dirname, 'node_modules/codemirror/theme'),
|
||||
to: 'codemirror/theme'
|
||||
}
|
||||
])
|
||||
],
|
||||
|
||||
resolve: {
|
||||
extensions: ['.tsx', '.ts', '.js']
|
||||
},
|
||||
node: {
|
||||
fs: 'empty'
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user