Skip to content
This repository has been archived by the owner on Jul 27, 2020. It is now read-only.

[RFR] Implement eject command #104

Merged
merged 2 commits into from
Mar 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 examples/minimal/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"module": "commonjs",
"moduleResolution": "node",
"target": "es5",
"rootDir": "./src",
"rootDir": ".",
"outDir": "dist",
"sourceMap": true,
"lib": ["es2015", "esnext.asynciterable", "es2017", "dom"],
Expand Down
258 changes: 72 additions & 186 deletions packages/yoga/src/cli/commands/build/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { existsSync, writeFileSync } from 'fs'
import { EOL } from 'os'
import * as path from 'path'
import * as ts from 'typescript'
import { findConfigFile, importYogaConfig } from '../../../config'
import { findFileByExtension } from '../../../helpers'
import { ConfigWithInfo } from '../../../types'
import { DEFAULTS } from '../../../yogaDefaults'
import {
renderIndexFile,
renderPrismaEjectFile,
renderSimpleIndexFile,
renderResolversIndex,
} from './renderers'

const diagnosticHost: ts.FormatDiagnosticsHost = {
getNewLine: () => ts.sys.newLine,
Expand All @@ -15,40 +19,15 @@ const diagnosticHost: ts.FormatDiagnosticsHost = {

export default () => {
const info = importYogaConfig()
const tsConfigPath = findConfigFile('tsconfig.json', { required: true })
const tsConfigContent = ts.readConfigFile(tsConfigPath, ts.sys.readFile)

if (tsConfigContent.error) {
throw new Error(
ts.formatDiagnosticsWithColorAndContext(
[tsConfigContent.error],
diagnosticHost,
),
)
}

const inputConfig = ts.parseJsonConfigFileContent(
tsConfigContent.config,
ts.sys,
info.projectDir,
undefined,
tsConfigPath,
)
const config = fixConfig(inputConfig, info.projectDir)
const config = readConfigFromTsConfig(info)

compile(config.fileNames, config.options)

if (!info.yogaConfig.ejectFilePath) {
const ejectFilePath = path.join(
info.projectDir,
path.dirname(DEFAULTS.ejectFilePath!),
'index.ts',
)
const ejectFilePath = writeEjectFiles(info, (filePath, content) => {
outputFile(filePath, content, config.options, info)
})

writeEntryPoint(info, ejectFilePath, config)
} else {
useEntryPoint(info, info.yogaConfig.ejectFilePath, config)
}
useEntryPoint(info, ejectFilePath, config)
}

function compile(rootNames: string[], options: ts.CompilerOptions) {
Expand All @@ -68,177 +47,41 @@ function compile(rootNames: string[], options: ts.CompilerOptions) {
}
}

/**
* Do post-processing on config options to support `ts-node`.
*/
function fixConfig(config: ts.ParsedCommandLine, projectDir: string) {
// Target ES5 output by default (instead of ES3).
if (config.options.target === undefined) {
config.options.target = ts.ScriptTarget.ES5
}

// Target CommonJS modules by default (instead of magically switching to ES6 when the target is ES6).
if (config.options.module === undefined) {
config.options.module = ts.ModuleKind.CommonJS
}

if (config.options.outDir === undefined) {
config.options.outDir = 'dist'
}

config.options.rootDir = projectDir

return config
}

function useEntryPoint(
info: ConfigWithInfo,
ejectFilePath: string,
config: ts.ParsedCommandLine,
) {
const indexFile = `
import yoga from '${getRelativePath(
path.dirname(ejectFilePath),
ejectFilePath,
)}'

async function main() {
const serverInstance = await yoga.server()

return yoga.startServer(serverInstance)
}

main()
`
const indexFile = renderIndexFile(ejectFilePath)
const indexFilePath = path.join(path.dirname(ejectFilePath), 'index.ts')

outputFile(indexFile, indexFilePath, config.options, info)
outputFile(indexFilePath, indexFile, config.options, info)
}

function writeEntryPoint(
export function writeEjectFiles(
info: ConfigWithInfo,
ejectFilePath: string,
config: ts.ParsedCommandLine,
writeFile: (filePath: string, content: string) => void,
) {
if (info.yogaConfig.ejectFilePath) {
return info.yogaConfig.ejectFilePath
}

const ejectFilePath = path.join(info.projectDir, DEFAULTS.ejectFilePath!)
const ejectFile = info.yogaConfig.prisma
? prismaIndexFile(path.dirname(ejectFilePath), info)
: simpleIndexfile(path.dirname(ejectFilePath), info)
? renderPrismaEjectFile(ejectFilePath, info)
: renderSimpleIndexFile(ejectFilePath, info)

outputFile(ejectFile, ejectFilePath, config.options, info)
writeFile(ejectFilePath, ejectFile)

const resolverIndexPath = path.join(info.yogaConfig.resolversPath, 'index.ts')

if (!existsSync(resolverIndexPath)) {
writeResolversIndexFile(info, resolverIndexPath, config)
}
}
const resolverIndexFile = renderResolversIndex(info)

function prismaIndexFile(filePath: string, info: ConfigWithInfo) {
return `
import { ApolloServer, makePrismaSchema, express } from 'yoga'
import datamodelInfo from '${getRelativePath(
filePath,
info.datamodelInfoDir!,
)}'
import { prisma } from '${getRelativePath(filePath, info.prismaClientDir!)}'
${
info.yogaConfig.contextPath
? `import context from '${getRelativePath(
filePath,
info.yogaConfig.contextPath,
)}'`
: ''
writeFile(resolverIndexPath, resolverIndexFile)
}
import * as types from '${getRelativePath(
filePath,
info.yogaConfig.resolversPath,
)}'


const schema = makePrismaSchema({
types,
prisma: {
datamodelInfo,
client: prisma
},
outputs: false
})

const apolloServer = new ApolloServer.ApolloServer({
schema,
${info.yogaConfig.contextPath ? 'context' : ''}
})
const app = express()

apolloServer.applyMiddleware({ app, path: '/' })

app.listen({ port: 4000 }, () => {
console.log(
\`🚀 Server ready at http://localhost:4000/\`,
)
})
`
}

function simpleIndexfile(filePath: string, info: ConfigWithInfo) {
return `\
import { ApolloServer, makeSchema, express } from 'yoga'
${
info.yogaConfig.contextPath
? `import context from '${getRelativePath(
filePath,
info.yogaConfig.contextPath,
)}'`
: ''
}
import * as types from '${getRelativePath(
filePath,
info.yogaConfig.resolversPath,
)}'

const schema = makeSchema({
types,
outputs: false
})

const apolloServer = new ApolloServer.ApolloServer({
schema,
${info.yogaConfig.contextPath ? 'context' : ''}
})

const app = express()

apolloServer.applyMiddleware({ app, path: '/' })

app.listen({ port: 4000 }, () => {
console.log(
\`🚀 Server ready at http://localhost:4000/\`,
)
})
`
}

function writeResolversIndexFile(
info: ConfigWithInfo,
resolverIndexPath: string,
config: ts.ParsedCommandLine,
) {
const resolversFile = findFileByExtension(
info.yogaConfig.resolversPath,
'.ts',
)
const resolverIndexFile = `\
${resolversFile
.map(
filePath =>
`export * from '${getRelativePath(
info.yogaConfig.resolversPath,
filePath,
)}'`,
)
.join(EOL)}
`
outputFile(resolverIndexFile, resolverIndexPath, config.options, info)
return ejectFilePath
}

export function getTranspiledPath(
Expand All @@ -253,8 +96,8 @@ export function getTranspiledPath(
return path.join(outDir, pathToJsFile)
}

export function getRelativePath(dir: string, filePath: string): string {
let relativePath = path.relative(dir, filePath)
export function getRelativePath(sourceDir: string, targetPath: string): string {
let relativePath = path.relative(sourceDir, targetPath)

if (!relativePath.startsWith('.')) {
relativePath = './' + relativePath
Expand All @@ -280,8 +123,8 @@ function transpileModule(
}

function outputFile(
fileContent: string,
filePath: string,
fileContent: string,
compilerOptions: ts.CompilerOptions,
info: ConfigWithInfo,
) {
Expand All @@ -294,3 +137,46 @@ function outputFile(

writeFileSync(outFilePath, transpiled)
}

function fixConfig(config: ts.ParsedCommandLine, projectDir: string) {
// Target ES5 output by default (instead of ES3).
if (config.options.target === undefined) {
config.options.target = ts.ScriptTarget.ES5
}

// Target CommonJS modules by default (instead of magically switching to ES6 when the target is ES6).
if (config.options.module === undefined) {
config.options.module = ts.ModuleKind.CommonJS
}

if (config.options.outDir === undefined) {
config.options.outDir = 'dist'
}

config.options.rootDir = projectDir

return config
}

export function readConfigFromTsConfig(info: ConfigWithInfo) {
const tsConfigPath = findConfigFile('tsconfig.json', { required: true })
const tsConfigContent = ts.readConfigFile(tsConfigPath, ts.sys.readFile)

if (tsConfigContent.error) {
throw new Error(
ts.formatDiagnosticsWithColorAndContext(
[tsConfigContent.error],
diagnosticHost,
),
)
}

const inputConfig = ts.parseJsonConfigFileContent(
tsConfigContent.config,
ts.sys,
info.projectDir,
undefined,
tsConfigPath,
)
return fixConfig(inputConfig, info.projectDir)
}
Loading