Skip to content

Commit

Permalink
feat: generate objects and arrays from raw source
Browse files Browse the repository at this point in the history
  • Loading branch information
danielroe authored and pi0 committed Jan 25, 2022
1 parent 7bcad10 commit beb4297
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 12 deletions.
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ pnpm install knitwork

## Usage

**Generating ESM syntax:**

```js
import { genImport, genExport } from 'knitwork'

Expand All @@ -47,6 +49,23 @@ console.log(genExport('pkg', ['a', 'b']))
console.log(genExport('pkg', { name: '*foo*', as: 'bar' }))
```

**Serializing JS objects:**

```js
import { genObjectFromRaw, genObjectFromRawEntries, genArrayFromRaw } from 'knitwork'

// { test: () => import("pkg") }
console.log(genObjectFromRaw({ test: '() => import("pkg")' }))

// { test: () => import("pkg") }
console.log(genObjectFromRaw([ ['test', '() => import("pkg")'] ]))

console.log(genObjectFromRawEntries(entries))

// [ 1, 2, () => import("pkg") ]
console.log(genArrayFromRaw(['1', '2', '() => import("pkg")']))
```

## 💻 Development

- Clone this repository
Expand Down
2 changes: 1 addition & 1 deletion src/esm.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

import { CodegenOptions } from './types'
import { genString } from './js'
import { genString } from './string'

// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
// https://tc39.es/ecma262/multipage/ecmascript-language-scripts-and-modules.html#sec-imports
Expand Down
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './esm'
export * from './js'
export * from './object'
export * from './string'
export * from './types'
9 changes: 0 additions & 9 deletions src/js.ts

This file was deleted.

47 changes: 47 additions & 0 deletions src/object.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { genString } from './string'

export function genObjectFromRaw (obj: Record<string, any>, indent = ''): string {
return genObjectFromRawEntries(Object.entries(obj), indent)
}

export function genArrayFromRaw (array: any[], indent = '') {
const newIdent = indent + ' '
return wrapInDelimiters(array.map(i => `${newIdent}${genRawValue(i, newIdent)}`), indent, '[]')
}

export function genObjectFromRawEntries (array: [key: string, value: any][], indent = '') {
const newIdent = indent + ' '
return wrapInDelimiters(array.map(([key, value]) => `${newIdent}${genObjectKey(key)}: ${genRawValue(value, newIdent)}`), indent, '{}')
}

// --- Internals ---

function wrapInDelimiters (lines: string[], indent = '', delimiters = '{}') {
if (!lines.length) {
return delimiters
}
const [start, end] = delimiters
return `${start}\n` + lines.join(',\n') + `\n${indent}${end}`
}

function genRawValue (value: unknown, indent = ''): string {
if (typeof value === 'undefined') {
return 'undefined'
}
if (value === null) {
return 'null'
}
if (Array.isArray(value)) {
return genArrayFromRaw(value, indent)
}
if (value && typeof value === 'object') {
return genObjectFromRaw(value, indent)
}
return value.toString()
}

const VALID_IDENTIFIER_RE = /^[$_]?[\w\d]*$/

function genObjectKey (key: string) {
return key.match(VALID_IDENTIFIER_RE) ? key : genString(key)
}
18 changes: 18 additions & 0 deletions src/string.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { CodegenOptions } from './types'

export function genString (input: string, opts: CodegenOptions = {}) {
const str = JSON.stringify(input)
if (!opts.singleQuotes) {
return JSON.stringify(input)
}
return `'${escapeString(str)}'`
}

// https://github.com/rollup/rollup/blob/master/src/utils/escapeId.ts
const NEEDS_ESCAPE_RE = /[\\'\r\n\u2028\u2029]/
const QUOTE_NEWLINE_RE = /(['\r\n\u2028\u2029])/g
const BACKSLASH_RE = /\\/g
export function escapeString (id: string): string {
if (!id.match(NEEDS_ESCAPE_RE)) { return id }
return id.replace(BACKSLASH_RE, '\\\\').replace(QUOTE_NEWLINE_RE, '\\$1')
}
57 changes: 56 additions & 1 deletion test/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expect, describe, it } from 'vitest'
import { genImport, genExport, genDynamicImport } from '../src'
import { genImport, genExport, genDynamicImport, genObjectFromRaw, genObjectFromRawEntries } from '../src'

const genImportTests = [
{ names: 'foo', code: 'import foo from "pkg";' },
Expand Down Expand Up @@ -53,3 +53,58 @@ describe('genDynamicImport', () => {
})
}
})

const genObjectFromRawTests = [
{
obj: {
a: 'null',
b: null,
c: undefined,
1: 'undefined',
2: true,
3: 'true',
'obj 1': '{ literal: () => "test" }',
'obj 2': { nested: { foo: '"bar"' } },
arr: ['1', '2', '3']
},
code: [
'{',
' 1: undefined,',
' 2: true,',
' 3: true,',
' a: null,',
' b: null,',
' c: undefined,',
' "obj 1": { literal: () => "test" },',
' "obj 2": {',
' nested: {',
' foo: "bar"',
' }',
' },',
' arr: [',
' 1,',
' 2,',
' 3',
' ]',
'}'
].join('\n')
}
]

describe('genObjectFromRaw', () => {
for (const t of genObjectFromRawTests) {
it(t.code, () => {
const code = genObjectFromRaw(t.obj)
expect(code).to.equal(t.code)
})
}
})

describe('genObjectFromRawEntries', () => {
for (const t of genObjectFromRawTests) {
it(t.code, () => {
const code = genObjectFromRawEntries(Object.entries(t.obj))
expect(code).to.equal(t.code)
})
}
})

0 comments on commit beb4297

Please sign in to comment.