Skip to content

Commit 2967fac

Browse files
authored
feat: 🎪 Playground! (#2)
1 parent 4470afb commit 2967fac

36 files changed

+4216
-2
lines changed

‎.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,5 @@ dist
102102

103103
# TernJS port file
104104
.tern-port
105+
106+
.DS_Store

‎README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ Inspired by [react-codemod](https://github.com/reactjs/react-codemod).
2929
- [ ] Built-in transformations need to support TypeScript
3030
- [ ] Built-in transformations need to support module systems other than ES module, and those without modules
3131
- [ ] Define an interface for transformation of template blocks (may use [`vue-eslint-parser`](https://github.com/mysticatea/vue-eslint-parser/) for this)
32-
- [ ] A playground for writing transformations
32+
- [x] A playground for writing transformations - `yarn playground` and visit http://localhost:3000
3333

3434
## Included Transformations
3535

‎package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"scripts": {
1212
"build": "tsc",
1313
"prepublishOnly": "tsc",
14+
"playground": "npm -C ./playground run dev",
1415
"test": "jest"
1516
},
1617
"repository": {

‎playground/api/app.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import Koa from 'koa'
2+
import http from 'http'
3+
import { router } from './router'
4+
import json from 'koa-json'
5+
import bodyParser from 'koa-bodyparser'
6+
import error from 'koa-error'
7+
8+
const app = new Koa()
9+
const server = http.createServer(app.callback())
10+
11+
app
12+
.use(error({
13+
env: 'development'
14+
}))
15+
.use(bodyParser({
16+
enableTypes: ['json', 'text']
17+
}))
18+
.use(json())
19+
.use(router.routes())
20+
.use(router.allowedMethods())
21+
22+
export {
23+
app,
24+
server
25+
}

‎playground/api/constants.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import path from "path"
2+
3+
export const API_PORT = process.env.API_PORT || process.env.PORT || 3002
4+
export const ROOT_DIR = path.resolve(__dirname, '../..')
5+
export const TRANS_DIR = path.resolve(ROOT_DIR, 'transformations')

‎playground/api/controllers.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import fg from 'fast-glob'
2+
import { TRANS_DIR, API_PORT, ROOT_DIR } from './constants'
3+
import path from 'path'
4+
5+
export async function getTransformations() {
6+
const files = await fg('*.ts', {
7+
cwd: TRANS_DIR,
8+
onlyFiles: true,
9+
})
10+
11+
// remove .ts extension and filter out index.ts
12+
return files.map((f) => f.slice(0, -3)).filter((f) => f !== 'index')
13+
}
14+
15+
export async function getMeta() {
16+
const transformations = await getTransformations()
17+
18+
const fixtures: any = {}
19+
20+
for (const t of transformations) {
21+
const files = await fg('*.{vue,ts,js}', {
22+
cwd: path.join(TRANS_DIR, '__testfixtures__', t),
23+
onlyFiles: true,
24+
})
25+
26+
if (files.length) {
27+
fixtures[t] = files
28+
}
29+
}
30+
31+
return {
32+
apiPort: API_PORT,
33+
rootPath: ROOT_DIR,
34+
transformations,
35+
fixtures,
36+
}
37+
}

‎playground/api/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { server } from './app'
2+
import './watcher'
3+
import { API_PORT } from './constants'
4+
5+
console.log(`Vue Codemode Playground API Started at http://localhost:${API_PORT}`)
6+
server.listen(API_PORT)

‎playground/api/router.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import Router from '@koa/router'
2+
import path from 'path'
3+
import fs from 'fs-extra'
4+
import { ROOT_DIR } from './constants'
5+
import { spawnSync } from 'child_process'
6+
import { getMeta } from './controllers'
7+
8+
const router = new Router()
9+
10+
router.get('/', (ctx) => {
11+
ctx.body = 'Hello'
12+
})
13+
14+
router.get('/meta', async (ctx) => {
15+
ctx.body = await getMeta()
16+
})
17+
18+
router.get('/files/(.*)', async (ctx) => {
19+
const filepath = path.join(ROOT_DIR, ctx.params[0])
20+
if (fs.existsSync(filepath)) {
21+
ctx.body = await fs.readFile(filepath, 'utf-8')
22+
} else {
23+
ctx.status = 404
24+
}
25+
})
26+
27+
router.post('/files/(.*)', async (ctx) => {
28+
const filepath = path.join(ROOT_DIR, ctx.params[0])
29+
await fs.ensureDir(path.dirname(filepath))
30+
await fs.writeFile(filepath, ctx.request.body, 'utf-8')
31+
ctx.status = 200
32+
})
33+
34+
router.post('/run/:trans', async (ctx) => {
35+
const name = ctx.params.trans
36+
const input = ctx.request.body
37+
const script = path.resolve(__dirname, 'transfrom.ts')
38+
39+
const result = spawnSync('ts-node', ['-T', script, name], {
40+
input,
41+
encoding: 'utf-8',
42+
})
43+
44+
const { stderr, stdout } = result
45+
46+
if (stderr) {
47+
ctx.body = `/* ERROR */\n\n${stderr}\n`
48+
} else {
49+
ctx.body = stdout
50+
}
51+
})
52+
53+
export { router }

‎playground/api/transfrom.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import runTransformation from '../../src/run-transformation'
2+
3+
const transfrom = process.argv[2]
4+
const options = JSON.parse(process.argv[3] || '{}')
5+
6+
let input = ''
7+
process.stdin.on('data', (e) => {
8+
input += e.toString()
9+
})
10+
11+
process.stdin.on('end', () => {
12+
try {
13+
console.log(
14+
runTransformation(
15+
{
16+
source: input,
17+
path: 'anonymous.vue',
18+
},
19+
require(`../../transformations/${transfrom}.ts`),
20+
options
21+
)
22+
)
23+
} catch (e) {
24+
console.error(e)
25+
}
26+
})

‎playground/api/watcher.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import WebSocket from 'ws'
2+
import { server } from './app'
3+
import chokidar from 'chokidar'
4+
import { TRANS_DIR } from './constants'
5+
import path from 'path'
6+
7+
const wss = new WebSocket.Server({ server })
8+
9+
const clients: WebSocket[] = []
10+
11+
wss.on('connection', (ws) => {
12+
clients.push(ws)
13+
ws.on('close', () => {
14+
clients.splice(clients.indexOf(ws), 1)
15+
})
16+
})
17+
18+
chokidar.watch(TRANS_DIR, {ignoreInitial: true}).on('all', (event, filepath) => {
19+
const relative = path.relative(TRANS_DIR, filepath)
20+
console.log(event, relative)
21+
clients.forEach((ws) => {
22+
ws.send(
23+
JSON.stringify({
24+
event,
25+
path: relative,
26+
})
27+
)
28+
})
29+
})

0 commit comments

Comments
 (0)