1
0
Fork 0
mirror of https://github.com/terribleplan/next.js.git synced 2024-01-19 02:48:18 +00:00

Implement tslint for core files (#5952)

Fixes #5845 

Implement tslint for core files

**What is this?**
Implements tslint for both next and next-server, but keeps standardjs/eslint for the .js files that are still there, we're gradually migrating to Typescript.

**How does it work?**
Before every commit (pre-commit) we execute the following `tslint` command:
`tslint -c tslint.json 'packages/**/*.ts`

**TSLint Rules**
In order to avoid as much changes as possible I marked some rules as false. This way we can improve the linter but making sure this step will not break things. (see tslint.json)

**Note**
After merging this PR, you'll need to update your dependencies since it adds tslint to package.json
This commit is contained in:
Anderson Leite 2018-12-31 05:44:27 -08:00 committed by Tim Neutkens
parent aabc72015c
commit 20fe65ce41
17 changed files with 122 additions and 72 deletions

View file

@ -33,7 +33,7 @@
"git add" "git add"
], ],
"*.ts": [ "*.ts": [
"standard --parser typescript-eslint-parser --plugin typescript --fix", "tslint -c tslint.json 'packages/**/*.ts'",
"git add" "git add"
], ],
"packages/**/bin/*": [ "packages/**/bin/*": [
@ -44,7 +44,7 @@
"standard": { "standard": {
"parser": "babel-eslint", "parser": "babel-eslint",
"ignore": [ "ignore": [
"packages/next-server/server/next-server.ts", "packages/**/*.ts",
"**/*.d.ts", "**/*.d.ts",
"**/node_modules/**", "**/node_modules/**",
"examples/with-ioc/**", "examples/with-ioc/**",
@ -89,6 +89,8 @@
"rimraf": "2.6.2", "rimraf": "2.6.2",
"standard": "11.0.1", "standard": "11.0.1",
"taskr": "1.1.0", "taskr": "1.1.0",
"tslint": "5.12.0",
"typescript": "3.1.1",
"typescript-eslint-parser": "21.0.1", "typescript-eslint-parser": "21.0.1",
"wait-port": "0.2.2", "wait-port": "0.2.2",
"wd": "1.10.3", "wd": "1.10.3",

View file

@ -2,7 +2,7 @@ export type ManifestItem = {
id: number|string, id: number|string,
name: string, name: string,
file: string, file: string,
publicPath: string publicPath: string,
} }
export type Manifest = {[moduleId: string]: ManifestItem[]} export type Manifest = {[moduleId: string]: ManifestItem[]}
@ -10,7 +10,7 @@ export type Manifest = {[moduleId: string]: ManifestItem[]}
type DynamicImportBundles = Set<ManifestItem> type DynamicImportBundles = Set<ManifestItem>
// Based on https://github.com/jamiebuilds/react-loadable/pull/132 // Based on https://github.com/jamiebuilds/react-loadable/pull/132
export function getDynamicImportBundles (manifest: Manifest, moduleIds: string[]): DynamicImportBundles { export function getDynamicImportBundles(manifest: Manifest, moduleIds: string[]): DynamicImportBundles {
return moduleIds.reduce((bundles: DynamicImportBundles, moduleId: string): DynamicImportBundles => { return moduleIds.reduce((bundles: DynamicImportBundles, moduleId: string): DynamicImportBundles => {
if (typeof manifest[moduleId] === 'undefined') { if (typeof manifest[moduleId] === 'undefined') {
return bundles return bundles

View file

@ -3,15 +3,16 @@ import { normalizePagePath } from './normalize-page-path'
export type BuildManifest = { export type BuildManifest = {
devFiles: string[], devFiles: string[],
pages: { pages: {
[page: string]: string[] [page: string]: string[],
} },
} }
export function getPageFiles (buildManifest: BuildManifest, page: string): string[] { export function getPageFiles(buildManifest: BuildManifest, page: string): string[] {
const normalizedPage = normalizePagePath(page) const normalizedPage = normalizePagePath(page)
const files = buildManifest.pages[normalizedPage] const files = buildManifest.pages[normalizedPage]
if (!files) { if (!files) {
// tslint:disable-next-line
console.warn(`Could not find files for ${normalizedPage} in .next/build-manifest.json`) console.warn(`Could not find files for ${normalizedPage} in .next/build-manifest.json`)
return [] return []
} }

View file

@ -2,19 +2,19 @@ import {join} from 'path'
import {CLIENT_STATIC_FILES_PATH, BUILD_MANIFEST, REACT_LOADABLE_MANIFEST, SERVER_DIRECTORY} from 'next-server/constants' import {CLIENT_STATIC_FILES_PATH, BUILD_MANIFEST, REACT_LOADABLE_MANIFEST, SERVER_DIRECTORY} from 'next-server/constants'
import {requirePage} from './require' import {requirePage} from './require'
function interopDefault (mod: any) { function interopDefault(mod: any) {
return mod.default || mod return mod.default || mod
} }
export async function loadComponents (distDir: string, buildId: string, pathname: string) { export async function loadComponents(distDir: string, buildId: string, pathname: string) {
const documentPath = join(distDir, SERVER_DIRECTORY, CLIENT_STATIC_FILES_PATH, buildId, 'pages', '_document') const documentPath = join(distDir, SERVER_DIRECTORY, CLIENT_STATIC_FILES_PATH, buildId, 'pages', '_document')
const appPath = join(distDir, SERVER_DIRECTORY, CLIENT_STATIC_FILES_PATH, buildId, 'pages', '_app') const appPath = join(distDir, SERVER_DIRECTORY, CLIENT_STATIC_FILES_PATH, buildId, 'pages', '_app')
let [buildManifest, reactLoadableManifest, Component, Document, App] = await Promise.all([ const [buildManifest, reactLoadableManifest, Component, Document, App] = await Promise.all([
require(join(distDir, BUILD_MANIFEST)), require(join(distDir, BUILD_MANIFEST)),
require(join(distDir, REACT_LOADABLE_MANIFEST)), require(join(distDir, REACT_LOADABLE_MANIFEST)),
interopDefault(requirePage(pathname, distDir)), interopDefault(requirePage(pathname, distDir)),
interopDefault(require(documentPath)), interopDefault(require(documentPath)),
interopDefault(require(appPath)) interopDefault(require(appPath)),
]) ])
return {buildManifest, reactLoadableManifest, Component, Document, App} return {buildManifest, reactLoadableManifest, Component, Document, App}

View file

@ -21,7 +21,7 @@ type ServerConstructor = {
dir?: string, dir?: string,
staticMarkup?: boolean, staticMarkup?: boolean,
quiet?: boolean, quiet?: boolean,
conf?: NextConfig conf?: NextConfig,
} }
export default class Server { export default class Server {
@ -35,11 +35,11 @@ export default class Server {
buildId: string, buildId: string,
generateEtags: boolean, generateEtags: boolean,
runtimeConfig?: {[key: string]: any}, runtimeConfig?: {[key: string]: any},
assetPrefix?: string assetPrefix?: string,
} }
router: Router router: Router
public constructor ({ dir = '.', staticMarkup = false, quiet = false, conf = null }: ServerConstructor = {}) { public constructor({ dir = '.', staticMarkup = false, quiet = false, conf = null }: ServerConstructor = {}) {
this.dir = resolve(dir) this.dir = resolve(dir)
this.quiet = quiet this.quiet = quiet
const phase = this.currentPhase() const phase = this.currentPhase()
@ -54,7 +54,7 @@ export default class Server {
this.renderOpts = { this.renderOpts = {
staticMarkup, staticMarkup,
buildId: this.buildId, buildId: this.buildId,
generateEtags generateEtags,
} }
// Only the `publicRuntimeConfig` key is exposed to the client side // Only the `publicRuntimeConfig` key is exposed to the client side
@ -66,7 +66,7 @@ export default class Server {
// Initialize next/config with the environment configuration // Initialize next/config with the environment configuration
envConfig.setConfig({ envConfig.setConfig({
serverRuntimeConfig, serverRuntimeConfig,
publicRuntimeConfig publicRuntimeConfig,
}) })
const routes = this.generateRoutes() const routes = this.generateRoutes()
@ -74,16 +74,17 @@ export default class Server {
this.setAssetPrefix(assetPrefix) this.setAssetPrefix(assetPrefix)
} }
private currentPhase (): string { private currentPhase(): string {
return PHASE_PRODUCTION_SERVER return PHASE_PRODUCTION_SERVER
} }
private logError(...args: any): void { private logError(...args: any): void {
if(this.quiet) return if (this.quiet) return
// tslint:disable-next-line
console.error(...args) console.error(...args)
} }
private handleRequest (req: IncomingMessage, res: ServerResponse, parsedUrl?: UrlWithParsedQuery): Promise<void> { private handleRequest(req: IncomingMessage, res: ServerResponse, parsedUrl?: UrlWithParsedQuery): Promise<void> {
// Parse url if parsedUrl not provided // Parse url if parsedUrl not provided
if (!parsedUrl || typeof parsedUrl !== 'object') { if (!parsedUrl || typeof parsedUrl !== 'object') {
const url: any = req.url const url: any = req.url
@ -104,26 +105,26 @@ export default class Server {
}) })
} }
public getRequestHandler () { public getRequestHandler() {
return this.handleRequest.bind(this) return this.handleRequest.bind(this)
} }
public setAssetPrefix (prefix?: string) { public setAssetPrefix(prefix?: string) {
this.renderOpts.assetPrefix = prefix ? prefix.replace(/\/$/, '') : '' this.renderOpts.assetPrefix = prefix ? prefix.replace(/\/$/, '') : ''
asset.setAssetPrefix(this.renderOpts.assetPrefix) asset.setAssetPrefix(this.renderOpts.assetPrefix)
} }
// Backwards compatibility // Backwards compatibility
public async prepare (): Promise<void> {} public async prepare(): Promise<void> {}
// Backwards compatibility // Backwards compatibility
private async close (): Promise<void> {} private async close(): Promise<void> {}
private setImmutableAssetCacheControl (res: ServerResponse) { private setImmutableAssetCacheControl(res: ServerResponse) {
res.setHeader('Cache-Control', 'public, max-age=31536000, immutable') res.setHeader('Cache-Control', 'public, max-age=31536000, immutable')
} }
private generateRoutes (): Route[] { private generateRoutes(): Route[] {
const routes: Route[] = [ const routes: Route[] = [
{ {
match: route('/_next/static/:path*'), match: route('/_next/static/:path*'),
@ -136,14 +137,14 @@ export default class Server {
} }
const p = join(this.distDir, CLIENT_STATIC_FILES_PATH, ...(params.path || [])) const p = join(this.distDir, CLIENT_STATIC_FILES_PATH, ...(params.path || []))
await this.serveStatic(req, res, p, parsedUrl) await this.serveStatic(req, res, p, parsedUrl)
} },
}, },
{ {
match: route('/_next/:path*'), match: route('/_next/:path*'),
// This path is needed because `render()` does a check for `/_next` and the calls the routing again // This path is needed because `render()` does a check for `/_next` and the calls the routing again
fn: async (req, res, _params, parsedUrl) => { fn: async (req, res, _params, parsedUrl) => {
await this.render404(req, res, parsedUrl) await this.render404(req, res, parsedUrl)
} },
}, },
{ {
// It's very important to keep this route's param optional. // It's very important to keep this route's param optional.
@ -154,8 +155,8 @@ export default class Server {
fn: async (req, res, params, parsedUrl) => { fn: async (req, res, params, parsedUrl) => {
const p = join(this.dir, 'static', ...(params.path || [])) const p = join(this.dir, 'static', ...(params.path || []))
await this.serveStatic(req, res, p, parsedUrl) await this.serveStatic(req, res, p, parsedUrl)
} },
} },
] ]
if (this.nextConfig.useFileSystemPublicRoutes) { if (this.nextConfig.useFileSystemPublicRoutes) {
@ -167,18 +168,18 @@ export default class Server {
match: route('/:path*'), match: route('/:path*'),
fn: async (req, res, _params, parsedUrl) => { fn: async (req, res, _params, parsedUrl) => {
const { pathname, query } = parsedUrl const { pathname, query } = parsedUrl
if(!pathname) { if (!pathname) {
throw new Error('pathname is undefined') throw new Error('pathname is undefined')
} }
await this.render(req, res, pathname, query, parsedUrl) await this.render(req, res, pathname, query, parsedUrl)
} },
}) })
} }
return routes return routes
} }
private async run (req: IncomingMessage, res: ServerResponse, parsedUrl: UrlWithParsedQuery) { private async run(req: IncomingMessage, res: ServerResponse, parsedUrl: UrlWithParsedQuery) {
try { try {
const fn = this.router.match(req, res, parsedUrl) const fn = this.router.match(req, res, parsedUrl)
if (fn) { if (fn) {
@ -206,7 +207,7 @@ export default class Server {
return sendHTML(req, res, html, {generateEtags}) return sendHTML(req, res, html, {generateEtags})
} }
public async render (req: IncomingMessage, res: ServerResponse, pathname: string, query: ParsedUrlQuery = {}, parsedUrl?: UrlWithParsedQuery): Promise<void> { public async render(req: IncomingMessage, res: ServerResponse, pathname: string, query: ParsedUrlQuery = {}, parsedUrl?: UrlWithParsedQuery): Promise<void> {
const url: any = req.url const url: any = req.url
if (isInternalUrl(url)) { if (isInternalUrl(url)) {
return this.handleRequest(req, res, parsedUrl) return this.handleRequest(req, res, parsedUrl)
@ -233,7 +234,7 @@ export default class Server {
return renderToHTML(req, res, pathname, query, {...result, ...opts}) return renderToHTML(req, res, pathname, query, {...result, ...opts})
} }
public async renderToHTML (req: IncomingMessage, res: ServerResponse, pathname: string, query: ParsedUrlQuery = {}): Promise<string|null> { public async renderToHTML(req: IncomingMessage, res: ServerResponse, pathname: string, query: ParsedUrlQuery = {}): Promise<string|null> {
try { try {
// To make sure the try/catch is executed // To make sure the try/catch is executed
const html = await this.renderToHTMLWithComponents(req, res, pathname, query, this.renderOpts) const html = await this.renderToHTMLWithComponents(req, res, pathname, query, this.renderOpts)
@ -250,30 +251,30 @@ export default class Server {
} }
} }
public async renderError (err: Error|null, req: IncomingMessage, res: ServerResponse, pathname: string, query: ParsedUrlQuery = {}): Promise<void> { public async renderError(err: Error|null, req: IncomingMessage, res: ServerResponse, pathname: string, query: ParsedUrlQuery = {}): Promise<void> {
res.setHeader('Cache-Control', 'no-cache, no-store, max-age=0, must-revalidate') res.setHeader('Cache-Control', 'no-cache, no-store, max-age=0, must-revalidate')
const html = await this.renderErrorToHTML(err, req, res, pathname, query) const html = await this.renderErrorToHTML(err, req, res, pathname, query)
if(html === null) { if (html === null) {
return return
} }
return this.sendHTML(req, res, html) return this.sendHTML(req, res, html)
} }
public async renderErrorToHTML (err: Error|null, req: IncomingMessage, res: ServerResponse, _pathname: string, query: ParsedUrlQuery = {}) { public async renderErrorToHTML(err: Error|null, req: IncomingMessage, res: ServerResponse, _pathname: string, query: ParsedUrlQuery = {}) {
return this.renderToHTMLWithComponents(req, res, '/_error', query, {...this.renderOpts, err}) return this.renderToHTMLWithComponents(req, res, '/_error', query, {...this.renderOpts, err})
} }
public async render404 (req: IncomingMessage, res: ServerResponse, parsedUrl?: UrlWithParsedQuery): Promise<void> { public async render404(req: IncomingMessage, res: ServerResponse, parsedUrl?: UrlWithParsedQuery): Promise<void> {
const url: any = req.url const url: any = req.url
const { pathname, query } = parsedUrl ? parsedUrl : parseUrl(url, true) const { pathname, query } = parsedUrl ? parsedUrl : parseUrl(url, true)
if(!pathname) { if (!pathname) {
throw new Error('pathname is undefined') throw new Error('pathname is undefined')
} }
res.statusCode = 404 res.statusCode = 404
return this.renderError(null, req, res, pathname, query) return this.renderError(null, req, res, pathname, query)
} }
public async serveStatic (req: IncomingMessage, res: ServerResponse, path: string, parsedUrl?: UrlWithParsedQuery): Promise<void> { public async serveStatic(req: IncomingMessage, res: ServerResponse, path: string, parsedUrl?: UrlWithParsedQuery): Promise<void> {
if (!this.isServeableUrl(path)) { if (!this.isServeableUrl(path)) {
return this.render404(req, res, parsedUrl) return this.render404(req, res, parsedUrl)
} }
@ -289,7 +290,7 @@ export default class Server {
} }
} }
private isServeableUrl (path: string): boolean { private isServeableUrl(path: string): boolean {
const resolved = resolve(path) const resolved = resolve(path)
if ( if (
resolved.indexOf(join(this.distDir) + sep) !== 0 && resolved.indexOf(join(this.distDir) + sep) !== 0 &&
@ -302,7 +303,7 @@ export default class Server {
return true return true
} }
private readBuildId (): string { private readBuildId(): string {
const buildIdFile = join(this.distDir, BUILD_ID_FILE) const buildIdFile = join(this.distDir, BUILD_ID_FILE)
try { try {
return fs.readFileSync(buildIdFile, 'utf8').trim() return fs.readFileSync(buildIdFile, 'utf8').trim()

View file

@ -1,5 +1,5 @@
import { posix } from 'path' import { posix } from 'path'
export function normalizePagePath (page: string): string { export function normalizePagePath(page: string): string {
// If the page is `/` we need to append `/index`, otherwise the returned directory root will be bundles instead of pages // If the page is `/` we need to append `/index`, otherwise the returned directory root will be bundles instead of pages
if (page === '/') { if (page === '/') {
page = '/index' page = '/index'

View file

@ -2,19 +2,20 @@ import {join} from 'path'
import {PAGES_MANIFEST, SERVER_DIRECTORY} from 'next-server/constants' import {PAGES_MANIFEST, SERVER_DIRECTORY} from 'next-server/constants'
import { normalizePagePath } from './normalize-page-path' import { normalizePagePath } from './normalize-page-path'
export function pageNotFoundError (page: string): Error { export function pageNotFoundError(page: string): Error {
const err: any = new Error(`Cannot find module for page: ${page}`) const err: any = new Error(`Cannot find module for page: ${page}`)
err.code = 'ENOENT' err.code = 'ENOENT'
return err return err
} }
export function getPagePath (page: string, distDir: string): string { export function getPagePath(page: string, distDir: string): string {
const serverBuildPath = join(distDir, SERVER_DIRECTORY) const serverBuildPath = join(distDir, SERVER_DIRECTORY)
const pagesManifest = require(join(serverBuildPath, PAGES_MANIFEST)) const pagesManifest = require(join(serverBuildPath, PAGES_MANIFEST))
try { try {
page = normalizePagePath(page) page = normalizePagePath(page)
} catch (err) { } catch (err) {
// tslint:disable-next-line
console.error(err) console.error(err)
throw pageNotFoundError(page) throw pageNotFoundError(page)
} }
@ -26,7 +27,7 @@ export function getPagePath (page: string, distDir: string): string {
return join(serverBuildPath, pagesManifest[page]) return join(serverBuildPath, pagesManifest[page])
} }
export function requirePage (page: string, distDir: string): any { export function requirePage(page: string, distDir: string): any {
const pagePath = getPagePath(page, distDir) const pagePath = getPagePath(page, distDir)
return require(pagePath) return require(pagePath)
} }

View file

@ -8,20 +8,20 @@ type Params = {[param: string]: string}
export type Route = { export type Route = {
match: (pathname: string|undefined) => false|Params, match: (pathname: string|undefined) => false|Params,
fn: (req: IncomingMessage, res: ServerResponse, params: Params, parsedUrl: UrlWithParsedQuery) => void fn: (req: IncomingMessage, res: ServerResponse, params: Params, parsedUrl: UrlWithParsedQuery) => void,
} }
export default class Router { export default class Router {
routes: Route[] routes: Route[]
constructor (routes: Route[] = []) { constructor(routes: Route[] = []) {
this.routes = routes this.routes = routes
} }
add (route: Route) { add(route: Route) {
this.routes.unshift(route) this.routes.unshift(route)
} }
match (req: IncomingMessage, res: ServerResponse, parsedUrl: UrlWithParsedQuery) { match(req: IncomingMessage, res: ServerResponse, parsedUrl: UrlWithParsedQuery) {
if (req.method !== 'GET' && req.method !== 'HEAD') { if (req.method !== 'GET' && req.method !== 'HEAD') {
return return
} }

View file

@ -3,7 +3,7 @@ import generateETag from 'etag'
import fresh from 'fresh' import fresh from 'fresh'
import { isResSent } from '../lib/utils' import { isResSent } from '../lib/utils'
export function sendHTML (req: IncomingMessage, res: ServerResponse, html: string, { generateEtags }: {generateEtags: boolean}) { export function sendHTML(req: IncomingMessage, res: ServerResponse, html: string, { generateEtags }: {generateEtags: boolean}) {
if (isResSent(res)) return if (isResSent(res)) return
const etag = generateEtags ? generateETag(html) : undefined const etag = generateEtags ? generateETag(html) : undefined

View file

@ -4,7 +4,7 @@ import send from 'send'
// since send doesn't support wasm yet // since send doesn't support wasm yet
send.mime.define({ 'application/wasm': ['wasm'] }) send.mime.define({ 'application/wasm': ['wasm'] })
export function serveStatic (req: IncomingMessage, res: ServerResponse, path: string): Promise<void> { export function serveStatic(req: IncomingMessage, res: ServerResponse, path: string): Promise<void> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
send(req, path) send(req, path)
.on('directory', () => { .on('directory', () => {

View file

@ -2,10 +2,10 @@ import { BLOCKED_PAGES } from 'next-server/constants'
const internalPrefixes = [ const internalPrefixes = [
/^\/_next\//, /^\/_next\//,
/^\/static\// /^\/static\//,
] ]
export function isInternalUrl (url: string): boolean { export function isInternalUrl(url: string): boolean {
for (const prefix of internalPrefixes) { for (const prefix of internalPrefixes) {
if (prefix.test(url)) { if (prefix.test(url)) {
return true return true
@ -15,6 +15,6 @@ export function isInternalUrl (url: string): boolean {
return false return false
} }
export function isBlockedPage (pathname: string): boolean { export function isBlockedPage(pathname: string): boolean {
return (BLOCKED_PAGES.indexOf(pathname) !== -1) return (BLOCKED_PAGES.indexOf(pathname) !== -1)
} }

View file

@ -9,7 +9,7 @@ const args = arg({
// Types // Types
'--help': Boolean, '--help': Boolean,
// Aliases // Aliases
'-h': '--help' '-h': '--help',
}) })
if (args['--help']) { if (args['--help']) {
@ -45,6 +45,7 @@ if (!existsSync(join(dir, 'pages'))) {
build(dir) build(dir)
.catch((err) => { .catch((err) => {
// tslint:disable-next-line
console.error('> Build error occurred') console.error('> Build error occurred')
printAndExit(err) printAndExit(err)
}) })

View file

@ -14,10 +14,11 @@ const args = arg({
// Aliases // Aliases
'-h': '--help', '-h': '--help',
'-p': '--port', '-p': '--port',
'-H': '--hostname' '-H': '--hostname',
}) })
if (args['--help']) { if (args['--help']) {
// tslint:disable-next-line
console.log(` console.log(`
Description Description
Starts the application in development mode (hot-code reloading, error Starts the application in development mode (hot-code reloading, error
@ -56,6 +57,7 @@ if (!existsSync(join(dir, 'pages'))) {
const port = args['--port'] || 3000 const port = args['--port'] || 3000
startServer({dir, dev: true}, port, args['--hostname']) startServer({dir, dev: true}, port, args['--hostname'])
.then(async (app) => { .then(async (app) => {
// tslint:disable-next-line
console.log(`> Ready on http://${args['--hostname'] || 'localhost'}:${port}`) console.log(`> Ready on http://${args['--hostname'] || 'localhost'}:${port}`)
await app.prepare() await app.prepare()
}) })
@ -63,18 +65,19 @@ startServer({dir, dev: true}, port, args['--hostname'])
if (err.code === 'EADDRINUSE') { if (err.code === 'EADDRINUSE') {
let errorMessage = `Port ${port} is already in use.` let errorMessage = `Port ${port} is already in use.`
const pkgAppPath = require('find-up').sync('package.json', { const pkgAppPath = require('find-up').sync('package.json', {
cwd: dir cwd: dir,
}) })
const appPackage = require(pkgAppPath) const appPackage = require(pkgAppPath)
if (appPackage.scripts) { if (appPackage.scripts) {
const nextScript = Object.entries(appPackage.scripts).find(scriptLine => scriptLine[1] === 'next') const nextScript = Object.entries(appPackage.scripts).find((scriptLine) => scriptLine[1] === 'next')
if (nextScript) { if (nextScript) {
errorMessage += `\nUse \`npm run ${nextScript[0]} -- -p <some other port>\`.` errorMessage += `\nUse \`npm run ${nextScript[0]} -- -p <some other port>\`.`
} }
} }
// tslint:disable-next-line
console.error(errorMessage) console.error(errorMessage)
} else { } else {
// tslint:disable-next-line
console.error(err) console.error(err)
} }
process.nextTick(() => process.exit(1)) process.nextTick(() => process.exit(1))

View file

@ -16,10 +16,11 @@ const args = arg({
// Aliases // Aliases
'-h': '--help', '-h': '--help',
'-s': '--silent', '-s': '--silent',
'-o': '--outdir' '-o': '--outdir',
}) })
if (args['--help']) { if (args['--help']) {
// tslint:disable-next-line
console.log(` console.log(`
Description Description
Exports the application for production deployment Exports the application for production deployment
@ -57,7 +58,7 @@ const options = {
silent: args['--silent'] || false, silent: args['--silent'] || false,
threads: args['--threads'], threads: args['--threads'],
concurrency: args['--concurrency'], concurrency: args['--concurrency'],
outdir: args['--outdir'] ? resolve(args['--outdir']) : join(dir, 'out') outdir: args['--outdir'] ? resolve(args['--outdir']) : join(dir, 'out'),
} }
exportApp(dir, options) exportApp(dir, options)

View file

@ -13,10 +13,11 @@ const args = arg({
// Aliases // Aliases
'-h': '--help', '-h': '--help',
'-p': '--port', '-p': '--port',
'-H': '--hostname' '-H': '--hostname',
}) })
if (args['--help']) { if (args['--help']) {
// tslint:disable-next-line
console.log(` console.log(`
Description Description
Starts the application in production mode. Starts the application in production mode.
@ -42,10 +43,12 @@ const dir = resolve(args._[0] || '.')
const port = args['--port'] || 3000 const port = args['--port'] || 3000
startServer({dir}, port, args['--hostname']) startServer({dir}, port, args['--hostname'])
.then(async (app) => { .then(async (app) => {
// tslint:disable-next-line
console.log(`> Ready on http://${args['--hostname']}:${port}`) console.log(`> Ready on http://${args['--hostname']}:${port}`)
await app.prepare() await app.prepare()
}) })
.catch((err) => { .catch((err) => {
// tslint:disable-next-line
console.error(err) console.error(err)
process.exit(1) process.exit(1)
}) })

View file

@ -3,11 +3,12 @@ import { join } from 'path'
import spawn from 'cross-spawn' import spawn from 'cross-spawn'
import arg from 'arg' import arg from 'arg'
['react', 'react-dom'].forEach(dependency => { ['react', 'react-dom'].forEach((dependency) => {
try { try {
// When 'npm link' is used it checks the clone location. Not the project. // When 'npm link' is used it checks the clone location. Not the project.
require.resolve(dependency) require.resolve(dependency)
} catch (err) { } catch (err) {
// tslint:disable-next-line
console.warn(`The module '${dependency}' was not found. Next.js requires that you include it in 'dependencies' of your 'package.json'. To add it, run 'npm install --save ${dependency}'`) console.warn(`The module '${dependency}' was not found. Next.js requires that you include it in 'dependencies' of your 'package.json'. To add it, run 'npm install --save ${dependency}'`)
} }
}) })
@ -17,7 +18,7 @@ const commands = [
'build', 'build',
'start', 'start',
'export', 'export',
defaultCommand defaultCommand,
] ]
const args = arg({ const args = arg({
@ -28,34 +29,36 @@ const args = arg({
// Aliases // Aliases
'-v': '--version', '-v': '--version',
'-h': '--help' '-h': '--help',
}, { }, {
permissive: true permissive: true,
}) })
// Version is inlined into the file using taskr build pipeline // Version is inlined into the file using taskr build pipeline
if (args['--version']) { if (args['--version']) {
// tslint:disable-next-line
console.log(`Next.js v${process.env.NEXT_VERSION}`) console.log(`Next.js v${process.env.NEXT_VERSION}`)
process.exit(0) process.exit(0)
} }
// Check if we are running `next <subcommand>` or `next` // Check if we are running `next <subcommand>` or `next`
const foundCommand = args._.find(cmd => commands.includes(cmd)) const foundCommand = args._.find((cmd) => commands.includes(cmd))
// Makes sure the `next <subcommand> --help` case is covered // Makes sure the `next <subcommand> --help` case is covered
// This help message is only showed for `next --help` // This help message is only showed for `next --help`
if (!foundCommand && args['--help']) { if (!foundCommand && args['--help']) {
// tslint:disable-next-line
console.log(` console.log(`
Usage Usage
$ next <command> $ next <command>
Available commands Available commands
${commands.join(', ')} ${commands.join(', ')}
Options Options
--version, -p Version number --version, -p Version number
--inspect Enable the Node.js inspector --inspect Enable the Node.js inspector
--help, -h Displays this message --help, -h Displays this message
For more information run a command with the --help flag For more information run a command with the --help flag
$ next build --help $ next build --help
@ -65,12 +68,13 @@ if (!foundCommand && args['--help']) {
const nodeArguments: string[] = [] const nodeArguments: string[] = []
if (args['--inspect']) { if (args['--inspect']) {
// tslint:disable-next-line
console.log('Passing "--inspect" to Node.js') console.log('Passing "--inspect" to Node.js')
nodeArguments.push('--inspect') nodeArguments.push('--inspect')
} }
const command = foundCommand || defaultCommand const command = foundCommand || defaultCommand
const forwardedArgs = args._.filter(arg => arg !== command) const forwardedArgs = args._.filter((arg) => arg !== command)
// Make sure the `next <subcommand> --help` case is covered // Make sure the `next <subcommand> --help` case is covered
if (args['--help']) { if (args['--help']) {
@ -92,12 +96,14 @@ const startProcess = () => {
if (signal === 'SIGKILL') { if (signal === 'SIGKILL') {
process.exit(137) process.exit(137)
} }
// tslint:disable-next-line
console.log(`got signal ${signal}, exiting`) console.log(`got signal ${signal}, exiting`)
process.exit(signal === 'SIGINT' ? 0 : 1) process.exit(signal === 'SIGINT' ? 0 : 1)
} }
process.exit(0) process.exit(0)
}) })
proc.on('error', (err) => { proc.on('error', (err) => {
// tslint:disable-next-line
console.error(err) console.error(err)
process.exit(1) process.exit(1)
}) })
@ -106,7 +112,7 @@ const startProcess = () => {
let proc = startProcess() let proc = startProcess()
function wrapper () { function wrapper() {
if (proc) { if (proc) {
proc.kill() proc.kill()
} }
@ -121,6 +127,7 @@ if (command === 'dev') {
const {watchFile} = require('fs') const {watchFile} = require('fs')
watchFile(`${process.cwd()}/${CONFIG_FILE}`, (cur: any, prev: any) => { watchFile(`${process.cwd()}/${CONFIG_FILE}`, (cur: any, prev: any) => {
if (cur.size > 0 || prev.size > 0) { if (cur.size > 0 || prev.size > 0) {
// tslint:disable-next-line
console.log(`\n> Found a change in ${CONFIG_FILE}, restarting the server...`) console.log(`\n> Found a change in ${CONFIG_FILE}, restarting the server...`)
// Don't listen to 'close' now since otherwise parent gets killed by listener // Don't listen to 'close' now since otherwise parent gets killed by listener
proc.removeAllListeners('close') proc.removeAllListeners('close')

30
tslint.json Normal file
View file

@ -0,0 +1,30 @@
{
"defaultSeverity": "error",
"extends": [
"tslint:recommended"
],
"jsRules": {},
"rules": {
"curly": false,
"no-empty": false,
"object-literal-sort-keys": false,
"variable-name": false,
"max-line-length": false,
"interface-over-type-literal": false,
"no-shadowed-variable": false,
"no-var-requires": false,
"semicolon": false,
"quotemark": false,
"ordered-imports": false,
"member-access": false,
"member-ordering": false
},
"rulesDirectory": [],
"linterOptions": {
"exclude": [
"**/node_modules/**",
"**/build/**",
"**/types/**"
]
}
}