Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a way to write and reference generated types #960

Merged
merged 34 commits into from
Aug 31, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
589b183
Add writing methods to host.
peterp Aug 12, 2020
d7a5c66
Add a way to keep additions to files unique.
peterp Aug 12, 2020
89acb01
Export appendFileUnique.
peterp Aug 12, 2020
89286d9
Make import-dir add reference to new types.
peterp Aug 12, 2020
8ff78b8
Fix import.
peterp Aug 12, 2020
375aec8
Test if file exists before reading.
peterp Aug 12, 2020
ebf9766
Fix references.
peterp Aug 12, 2020
9ea3e91
Fix import dir test.
peterp Aug 12, 2020
fe06f70
Add a way to generate types for routes.*().
peterp Aug 12, 2020
041a8ba
Merge branch 'main' into pp-write-generated-types
peterp Aug 12, 2020
7e8bab6
Move babel plugins.
peterp Aug 19, 2020
55c58f8
Add some notes about the plugins.
peterp Aug 19, 2020
880fb06
Add way to update the central type def file.
peterp Aug 19, 2020
0a67c20
Add way to write a single generated type.
peterp Aug 19, 2020
be791f6
Add ability to grab paths from hosts.
peterp Aug 19, 2020
f01dc20
Move defaultHost into seperate file for mocking.
peterp Aug 19, 2020
bc7522c
Update imports for DefaultHost.
peterp Aug 19, 2020
e1c271b
Use mocks for GenerateTypes tests.
peterp Aug 19, 2020
734507f
Use mocks for GenerateTypes tests.
peterp Aug 19, 2020
726f6a9
Test import-dir type def generators.
peterp Aug 19, 2020
68158ac
Remove generator code from model.
peterp Aug 20, 2020
4f0b40f
Generate types for routes.
peterp Aug 20, 2020
44ed904
Fix tests.
peterp Aug 20, 2020
53235c4
Use babel plugin.
peterp Aug 20, 2020
3d778cb
Fix imports.
peterp Aug 20, 2020
147510e
Merge branch 'pp-write-generated-types' of github.com:redwoodjs/redwo…
peterp Aug 20, 2020
2ace757
Fix eslint ignore pattern.
peterp Aug 20, 2020
ed73b8f
Generate page imports.
peterp Aug 20, 2020
186ab6b
Use none-cached router from project.
peterp Aug 28, 2020
7def7fe
Add timestamp to generated content.
peterp Aug 28, 2020
b5fe6b8
Merge remote-tracking branch 'origin/main' into pp-write-generated-types
peterp Aug 28, 2020
6a87cd2
Settle on a proper path.
peterp Aug 31, 2020
1f0ca26
Fix tests.
peterp Aug 31, 2020
858b475
Merge branch 'main' into pp-write-generated-types
peterp Aug 31, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
dist
fixtures
packages/api/importAll.macro.js
packages/core/src/__tests__/__fixtures__/**/*
packages/core/**/__fixtures__/**/*
packages/core/config/storybook/**/*
34 changes: 17 additions & 17 deletions packages/core/config/babel-preset.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
const fs = require('fs')

/**
* This is the babel preset used `create-redwood-app`
*/

const { getPaths } = require('@redwoodjs/internal')
const { getProject } = require('@redwoodjs/structure')

const TARGETS_NODE = '12.16.1'
// Warning! Use the minor core-js version: "corejs: '3.6'", instead of "corejs: 3",
Expand All @@ -13,7 +11,8 @@ const TARGETS_NODE = '12.16.1'
const CORE_JS_VERSION = '3.6'

module.exports = () => {
const paths = getPaths()
const project = getProject()
const paths = project.host.paths

return {
presets: ['@babel/preset-react', '@babel/preset-typescript'],
Expand Down Expand Up @@ -82,15 +81,7 @@ module.exports = () => {
],
},
],
[
require('../dist/babel-plugin-redwood-import-dir'),
{
generateTypesPath: paths.types,
host: {
writeFileSync: fs.writeFileSync,
},
},
],
[require('../dist/babelPlugins/babel-plugin-redwood-import-dir')],
],
},
// ** WEB **
Expand All @@ -100,7 +91,7 @@ module.exports = () => {
[
'@babel/preset-env',
{
// the targets are set in web/package.json
// the targets are set in <userProject>/web/package.json
useBuiltIns: 'usage',
corejs: {
version: CORE_JS_VERSION,
Expand Down Expand Up @@ -153,19 +144,28 @@ module.exports = () => {
// ** Files ending in `Cell.[js,ts]` **
{
test: /.+Cell.(js|tsx)$/,
plugins: [require('../dist/babel-plugin-redwood-cell')],
plugins: [require('../dist/babelPlugins/babel-plugin-redwood-cell')],
},
// Automatically import files in `./web/src/pages/*` in to
// the `./web/src/Routes.[ts|jsx]` file.
{
test: ['./web/src/Routes.js', './web/src/Routes.tsx'],
plugins: [require('../dist/babel-plugin-redwood-routes-auto-loader')],
plugins: [
[
require('../dist/babelPlugins/babel-plugin-redwood-routes-auto-loader'),
{
project,
},
],
],
},
// ** Files ending in `Cell.mock.[js,ts]` **
// Automatically determine keys for saving and retrieving mock data.
{
test: /.+Cell.mock.(js|ts)$/,
plugins: [require('../dist/babel-plugin-redwood-mock-cell-data')],
plugins: [
require('../dist/babelPlugins/babel-plugin-redwood-mock-cell-data'),
],
},
],
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import path from 'path'
import pluginTester from 'babel-plugin-tester'
import plugin from '../babel-plugin-redwood-import-dir'

const mockReaddirSync = jest.fn(() => [
'import-dir-services.d.ts',
'import-dir-graphql.d.ts',
])
const mockWriteFileSync = jest.fn()

jest.mock('@redwoodjs/structure', () => {
return {
DefaultHost: jest.fn().mockImplementation(() => ({
readdirSync: mockReaddirSync,
writeFileSync: mockWriteFileSync,
paths: {
types: '/fake/project/node_modules/@types/@redwoodjs/generated',
},
})),
}
})

describe('babel plugin redwood import dir', () => {
pluginTester({
plugin,
pluginName: 'babel-plugin-redwood-import-dir',
fixtures: path.join(__dirname, '__fixtures__/import-dir'),
})

afterAll(() => {
expect(mockWriteFileSync.mock.calls[0][1]).toContain(
`/fake/project/node_modules/@types/@redwoodjs/generated/import-dir-services.d.ts`
)

expect(mockWriteFileSync.mock.calls[0][1]).toContain(
`declare module 'src/__fixtures__/**/*.{js,ts}'`
)

expect(mockWriteFileSync.mock.calls[1][0]).toContain(
`/fake/project/node_modules/@types/@redwoodjs/index.d.ts`
)
expect(mockWriteFileSync.mock.calls[1][1]).toContain(
`/// <reference path="./types/import-dir-services.d.ts" />`
)

jest.clearAllMocks()
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import pluginTester from 'babel-plugin-tester'
import plugin from '../babel-plugin-redwood-mock-cell-data'

describe('babel plugin redwood mock cell data', () => {
const __fixtures__ = path.resolve(__dirname, '../../../../__fixtures__') //?
const __fixtures__ = path.resolve(__dirname, '../../../../../__fixtures__') //?

pluginTester({
plugin,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,23 @@
import path from 'path'

import pluginTester from 'babel-plugin-tester'

import plugin from '../babel-plugin-redwood-routes-auto-loader'
import { getProject } from '@redwoodjs/structure'

const mockReaddirSync = jest.fn(() => ['routes.d.ts'])
const mockWriteFileSync = jest.fn()

jest.mock('@redwoodjs/structure', () => {
return {
...jest.requireActual('@redwoodjs/structure'),
DefaultHost: jest.fn().mockImplementation(() => ({
readdirSync: mockReaddirSync,
writeFileSync: mockWriteFileSync,
paths: {
types: '/fake/project/node_modules/@types/@redwoodjs/generated',
},
})),
}
})

jest.mock('@redwoodjs/internal', () => ({
getPaths: () => {
Expand Down Expand Up @@ -44,8 +59,29 @@ jest.mock('@redwoodjs/internal', () => ({
},
}))

pluginTester({
plugin,
pluginName: 'babel-plugin-redwood-routes-auto-loader',
fixtures: path.join(__dirname, '__fixtures__/routes-auto-loader'),
describe('routes auto loader', () => {
const exampleTodoPath = path.resolve(
__dirname,
'../../../../../__fixtures__/example-todo-main'
)
const project = getProject(exampleTodoPath)

pluginTester({
plugin,
pluginName: 'babel-plugin-redwood-routes-auto-loader',
pluginOptions: {
project,
},
fixtures: path.join(__dirname, '__fixtures__/routes-auto-loader'),
})

afterAll(() => {
expect(mockWriteFileSync.mock.calls[0][0]).toContain(`routes.d.ts`)
expect(mockWriteFileSync.mock.calls[0][1]).toContain(`home: () => "/"`)
expect(mockWriteFileSync.mock.calls[1][0]).toContain(`index.d.ts`)
expect(mockWriteFileSync.mock.calls[1][1]).toContain(
`/// <reference path="./generated/routes.d.ts" />`
)
jest.clearAllMocks()
})
})
27 changes: 27 additions & 0 deletions packages/core/src/babelPlugins/__tests__/generateTypes.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { generateTypeDefIndex } from '../generateTypes'

const mockReaddirSync = jest.fn(() => [
'import-dir-service.d.ts',
'import-dir-graphql.d.ts',
])
const mockWriteFileSync = jest.fn()

jest.mock('@redwoodjs/structure', () => {
return {
DefaultHost: jest.fn().mockImplementation(() => ({
readdirSync: mockReaddirSync,
writeFileSync: mockWriteFileSync,
paths: {
types: '/fake/project/node_modules/@types/@redwoodjs/generated',
},
})),
}
})

test('generates index.d.ts file correctly', () => {
generateTypeDefIndex()
expect(mockWriteFileSync.mock.calls[0][0]).toContain('index.d.ts')
expect(mockWriteFileSync.mock.calls[0][1]).toContain(
`import-dir-service.d.ts`
)
})
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,11 @@ import path from 'path'

import glob from 'glob'
import type { PluginObj, types } from '@babel/core'
import type { Host } from '@redwoodjs/structure'

export const generateTypes = (modulePath: string) => {
// TODO:
// This implementation is a bit lacking:
// 1. We receive the resolved path instead of the aliases path,
// so we monkey patch it by replacing `../` with `src/`.
// 2. We don't provide any types beyond the module, which is fine since
// people aren't going to be using those.
return `// @ts-expect-error\ndeclare module '${modulePath.replace(
'../',
'src/'
)}';`
}
import { generateTypeDef, generateTypeDefIndex } from './generateTypes'

/**
* This babel plugin will search for import statements that include star `*`
* This babel plugin will search for import statements that include star `**`
* in the source part of the statement is a glob, the files that are matched are imported,
* and appended to an object.
*
Expand All @@ -33,16 +21,13 @@ export const generateTypes = (modulePath: string) => {
* // services.nested_c = require('src/services/nested/c.js')
* ```
*/
export default function (
{ types: t }: { types: typeof types },
options: { generateTypesPath: string; host: Host }
): PluginObj {
export default function ({ types: t }: { types: typeof types }): PluginObj {
return {
name: 'babel-plugin-redwood-import-dir',
visitor: {
ImportDeclaration(p, state: { file?: any }) {
// This code will only run when we find an import statement that includes a `*`.
if (!p.node.source.value.includes('*')) {
// This code will only run when we find an import statement that includes a `**`.
if (!p.node.source.value.includes('**')) {
return
}

Expand Down Expand Up @@ -115,15 +100,13 @@ export default function (
// - import importName from "dirPath"
p.remove()

if (options.host.writeFileSync) {
options.host.writeFileSync(
path.join(
options.generateTypesPath,
`import-dir-${importName}.d.ts`
),
generateTypes(importGlob)
)
}
// GenerateTypes
const typeDefContent = `
// @ts-expect-error
declare module '${importGlob.replace('../', 'src/')}';
`
generateTypeDef(`import-dir-${importName}.d.ts`, typeDefContent)
generateTypeDefIndex()
},
},
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
import type { PluginObj, types } from '@babel/core'

import { processPagesDir } from '@redwoodjs/internal'
import { generateTypeDef, generateTypeDefIndex } from './generateTypes'
import { RWProject } from '@redwoodjs/structure'

interface PluginOptions {
project: RWProject
}

export default function ({ types: t }: { types: typeof types }): PluginObj {
export default function (
{ types: t }: { types: typeof types },
{ project }: PluginOptions
): PluginObj {
let pages = processPagesDir()

return {
Expand All @@ -22,6 +32,37 @@ export default function ({ types: t }: { types: typeof types }): PluginObj {
Program: {
enter() {
pages = processPagesDir()

// Produces:
// routes.home: () => "/home"
// routes.aboutUs: () => "/about-us"
const availableRoutes = project
.getRouter()
.routes.filter((r) => !r.isNotFound)
.map((r) => `${r.name}: () => "${r.path}"`)

const pageImports = pages.map(
(page) => `import type ${page.const}Type from '${page.importPath}'`
)
const pageGlobals = pages.map(
(page) => `const ${page.const}: typeof ${page.const}Type`
)

const typeDefContent = `
declare module '@redwoodjs/router' {
interface AvailableRoutes {
${availableRoutes.join('\n')}
}
}

${pageImports.join('\n')}
declare global {
${pageGlobals.join('\n')}
}
`

generateTypeDef('routes.d.ts', typeDefContent)
generateTypeDefIndex()
},
exit(p) {
if (pages.length === 0) {
Expand Down
Loading