Skip to content

Commit

Permalink
feat: support loader with jsdoc support
Browse files Browse the repository at this point in the history
  • Loading branch information
pi0 committed Mar 23, 2021
1 parent 97960ad commit b31b4af
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 13 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ node_modules
coverage
dist
.tmp
/*.js
/*.d.ts
9 changes: 8 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
".": {
"import": "./dist/magic-schema.mjs",
"require": "./dist/magic-schema.js"
},
"./loader": {
"require": "./dist/loader.js"
}
},
"main": "./dist/magic-schema.js",
Expand All @@ -26,9 +29,13 @@
"release": "yarn test && standard-version && git push --follow-tags && npm publish",
"test": "yarn lint && jest"
},
"dependencies": {},
"devDependencies": {
"@babel/standalone": "^7.13.12",
"@babel/template": "^7.12.13",
"@babel/types": "^7.13.12",
"@nuxtjs/eslint-config-typescript": "latest",
"@types/babel__standalone": "^7.1.3",
"@types/babel__traverse": "^7.11.1",
"@types/jest": "latest",
"@types/node": "latest",
"@vitejs/plugin-vue": "^1.1.5",
Expand Down
17 changes: 12 additions & 5 deletions playground/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
</div>
</div>
<div class="block-content">
<Editor :value="state.input" @update:value="state.input = $event" />
<Editor :value="state.input" language="typescript" @update:value="state.input = $event" />
</div>
</div>
<!-- Tabs -->
Expand All @@ -30,7 +30,7 @@
<div class="block-title">
<div class="flex cursor-grab">
<div
v-for="tab in ['types', 'schema']"
v-for="tab in ['types', 'schema', 'loader']"
:key="tab"
class="tab"
:class="[tab == state.activeTab ? 'bg-gray-400' : 'bg-gray-200']"
Expand All @@ -48,15 +48,20 @@
<div v-if="state.activeTab === 'types'" class="block-content">
<Editor :value="types" read-only language="typescript" />
</div>
<!-- Loader -->
<div v-if="state.activeTab === 'loader'" class="block-content">
<Editor :value="transpiled" read-only language="typescript" />
</div>
</div>
</main>
</div>
</template>

<script>
import 'virtual:windi.css'
import { defineComponent, defineAsyncComponent } from 'vue'
import { defineComponent, defineAsyncComponent, watch } from 'vue'
import { resolveSchema, generateDts } from '../src'
import { transform } from '../src/loader'
import { evaluateSource, defaultInput, persistedState, safeComputed } from './utils'
import LoadingComponent from './components/Loading.vue'
Expand All @@ -73,14 +78,16 @@ export default defineComponent({
input: defaultInput
})
const parsedInput = safeComputed(() => evaluateSource(state.input))
const transpiled = safeComputed(() => transform(state.input))
const parsedInput = safeComputed(() => evaluateSource(transpiled.value))
const schema = safeComputed(() => resolveSchema(parsedInput.value))
const types = safeComputed(() => generateDts(schema.value))
return {
state,
schema,
types
types,
transpiled
}
}
})
Expand Down
13 changes: 7 additions & 6 deletions playground/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,15 @@ export const defaultInput = `
export default {
name: 'vulcan',
price: 12.5,
checked: {
$default: false,
$schema: {
description: 'checked state'
}
},
/**
* checked state
* If is null, will use last checked status
*/
checked: false,
dimensions: {
/** width in px */
width: 5,
/** width in px */
height: 10
},
tags: {
Expand Down
3 changes: 3 additions & 0 deletions playground/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import windiCSS from 'vite-plugin-windicss'

// https://vitejs.dev/config
export default defineConfig({
define: {
'process.env' : '{}'
},
plugins: [
vue(),
windiCSS({
Expand Down
90 changes: 90 additions & 0 deletions src/loader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { transform as babelTransform } from '@babel/standalone/babel.min.js'
import type { PluginObj } from '@babel/core'
import * as t from '@babel/types'

function jsdocSchema () {
return <PluginObj>{
visitor: {
ObjectProperty (p) {
if (p.node.leadingComments) {
const lines = p.node.leadingComments
.filter(c => c.type === 'CommentBlock')
.map(c => c.value.split('\n').map(l => l.replace(/^[\s*]+|[\s*]$/, '')))
.flat()
.filter(Boolean)

const comments: string[] = []
while (lines.length && !lines[0].startsWith('@')) {
comments.push(lines.shift())
}

const blockTags: Record<string, string> = {}
let lastTag = null
for (const line of lines) {
const m = line.match(/@(\w+) ?(.*)/)
if (m) {
blockTags[m[1]] = m[2]
lastTag = m[1]
} else {
blockTags[lastTag] =
(blockTags[lastTag] ? blockTags[lastTag] + '\n' : '') + line
}
}

const schema: any = {}
if (comments.length) {
schema.title = comments.shift()
}
if (comments.length) {
schema.description = comments.join('\n')
}
for (const tag in blockTags) {
schema[tag] = blockTags[tag]
}

const schemaAstProps =
Object.entries(schema).map(e => t.objectProperty(t.identifier(e[0]), t.stringLiteral(e[1] as string)))
const schemaAst = t.objectExpression(schemaAstProps)

if (p.node.value.type === 'ObjectExpression') {
const schemaProp = p.node.value.properties.find(prop =>
('key' in prop) && prop.key.type === 'Identifier' && prop.key.name === '$schema'
)
if (schemaProp && 'value' in schemaProp) {
if (schemaProp.value.type === 'ObjectExpression') {
// Object has $schema
schemaProp.value.properties.push(...schemaAstProps)
} else {
// Object has $schema which is not an object
// SKIP
}
} else {
// Object has not $schema
p.node.value.properties.unshift(t.objectProperty(t.identifier('$schema'), schemaAst))
}
} else {
// Literal value
p.node.value = t.objectExpression([
t.objectProperty(t.identifier('$default'), p.node.value),
t.objectProperty(t.identifier('$schema'), schemaAst)
])
}
p.node.leadingComments = []
}
}
}
}
}

export function transform (src) {
const res = babelTransform(src, {
filename: 'src.ts',
presets: [
'typescript'
],
plugins: [
jsdocSchema
]
})
return res.code
}
23 changes: 22 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,11 @@
dependencies:
"@babel/helper-plugin-utils" "^7.12.13"

"@babel/standalone@^7.13.12":
version "7.13.12"
resolved "https://registry.yarnpkg.com/@babel/standalone/-/standalone-7.13.12.tgz#a7065c30bd8511cd48c99193bd69e7c596bbbc44"
integrity sha512-2Xb4B3EdCmgwo7Q3WVHoLfZh0Jg1AvgjWF3e2kLGZ3Yg6EosD0g6qzMZJHZ2sCoEI/g7/bjphSMTqjQDZc6SkA==

"@babel/template@^7.12.13", "@babel/template@^7.3.3":
version "7.12.13"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.13.tgz#530265be8a2589dbb37523844c5bcb55947fb327"
Expand Down Expand Up @@ -302,6 +307,15 @@
lodash "^4.17.19"
to-fast-properties "^2.0.0"

"@babel/types@^7.13.12":
version "7.13.12"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.12.tgz#edbf99208ef48852acdff1c8a681a1e4ade580cd"
integrity sha512-K4nY2xFN4QMvQwkQ+zmBDp6ANMbVNw6BbxWmYA4qNjhR9W+Lj/8ky5MEY2Me5r+B2c6/v6F53oMndG+f9s3IiA==
dependencies:
"@babel/helper-validator-identifier" "^7.12.11"
lodash "^4.17.19"
to-fast-properties "^2.0.0"

"@bcoe/v8-coverage@^0.2.3":
version "0.2.3"
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
Expand Down Expand Up @@ -657,6 +671,13 @@
dependencies:
"@babel/types" "^7.0.0"

"@types/babel__standalone@^7.1.3":
version "7.1.3"
resolved "https://registry.yarnpkg.com/@types/babel__standalone/-/babel__standalone-7.1.3.tgz#e275639469c056f7cecc4963c88a803f028236dd"
integrity sha512-gj2Uvh8NT7+/0liAKMBtAqWspdg1LGZ471AD9Rdvrg1mB0q45qnl21dQPGz/ZtMYRgem0Aj/CUVQYHP3QDb5Gw==
dependencies:
"@babel/core" "^7.1.0"

"@types/babel__template@*":
version "7.4.0"
resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.0.tgz#0c888dd70b3ee9eebb6e4f200e809da0076262be"
Expand All @@ -665,7 +686,7 @@
"@babel/parser" "^7.1.0"
"@babel/types" "^7.0.0"

"@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6":
"@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6", "@types/babel__traverse@^7.11.1":
version "7.11.1"
resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.11.1.tgz#654f6c4f67568e24c23b367e947098c6206fa639"
integrity sha512-Vs0hm0vPahPMYi9tDjtP66llufgO3ST16WXaSTtDGEl9cewAl3AibmxWw6TINOqHPT9z0uABKAYjT9jNSg4npw==
Expand Down

0 comments on commit b31b4af

Please sign in to comment.