Skip to content

Commit

Permalink
Merge pull request #6 from barinbritva/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
barinbritva authored Mar 4, 2021
2 parents 77857fc + f0b6b11 commit a2ed5e4
Show file tree
Hide file tree
Showing 17 changed files with 141 additions and 54 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
uses: actions/checkout@v2

- name: Install dependencies
run: npm install
run: npm ci

- name: Run build
run: npm run build
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Changed
- Windows support (by rewriting tasks from bash to js).
- Project option: `package` for `npm` or any `app`.
- Refactoring on `tsconfig` and app generator logic.

## [1.0.9] - 2020-09-02
### Added
Expand Down
25 changes: 13 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
# Init TypeScript App
[![License](https://img.shields.io/npm/l/micromatch?style=flat-square)](https://github.com/barinbritva/init-typescript-app/blob/master/LICENSE)

This is clean framework/technology agnostic `TypeScript` setup.
## 🌟 Motivation

First of all, it's written to help quickly create `npm` packages in TypeScript. With `init-typescript-app` you can think only about your project. All other things `init-typescript-app` will take care of the rest.
You are in the right repo if you want to:

It's also a good idea to use it like a starting point for any of your projects.
* quickly get pure, technology agnostic `TypeScript` setup for further customization;
* easily create your own `npm` package.

## 📦 What in the box
## 📦 Features

* **Customizable TypeScript configuration.** If you just want to make your project work in TypeScript and not get a lot of difficulties which may be related with it, you can choose `base mode`. _By the way, it's the greatest choice if you just start learning TypeScript._ For advanced TypeScript users there is `advanced node` which includes all kinds of checks.

* **Easy and quick publication.** No more fuss with entering the same `npm` and `git` commands, no more mistakes during the publication process. Make your package publication happen by running one command.

* **Other features are on their way.** Please, read about upcoming features in [roadmap](#-roadmap) section.
* **Clean setup.** No dependencies. Only configured TypeScript with `production` and `development` build modes;
* **Tune compiler strictness.** Choose `base mode` to get TypeScript basic functionality or `advanced mode` to enable all kinds of checks;
* **Easy publication _(optional)_.** No more fuss with package publication to `npm`;
* **More features soon.** Please, read about upcoming features in [roadmap](#-roadmap) section.

## 🚀 Launch your project
### Quick overview
Expand Down Expand Up @@ -78,11 +78,12 @@ function doMyStuff (): number {
That's why these checks are switched off for development. You won't get these errors in your build, but an IDE will still show you errors so that you don't forget to fix them. This is a really convenient approach.

## 📮 Publish your package
When the first version of your package is ready to see the world, it's time to publish it.

If you chose `npm package` as your project type, you can make your package publication happen by running one command.

_Just run `npm run release` from the root directory of your project. That's it!_

But before you start, please, check out next information in this section.
Before you start, please, check out next information in this section.

### Check your git setup
To use publication features it's necessary for your project to be a `git` repository.
Expand Down Expand Up @@ -141,7 +142,7 @@ _In other words, follow your goals!_

### Troubleshooting
* In rare cases it's possible to get errors from somewhere of `node_modules/**/*d.ts`. It means some third-party library typings are broken. If you run into this problem you can solve it by adding `"skipDefaultLibCheck": true` to `compilerOptions` of `tsconfig.json`. _More information about `tsconfig` will be available soon._
* If you chose `advanced` type checking, but run into the wall with it, you can rollback to `base` mode in two ways. The first way is to open `tsconfig.json` and invert a value of numerous options listed there. You can use hints from an IDE to detect exactly which options give you troubles. You are also able to toggle values one by one to find issuer. You can return toggled values when you will become more experienced. The second is to rollback to `base` mode permanently. To do that just remove `tsconfig.json`, and rename `tsconfig-base.json` to `tsconfig.json`.
* If you chose `advanced` type checking, but run into the wall with it, you can rollback to `base` mode in two ways. The first way is to open `tsconfig.json` and invert a value of numerous options listed under `// Checks` comment. You can use hints from an IDE to detect exactly which options give you troubles. You are also able to toggle values one by one to find issuer. You can return toggled values when you will become more experienced. The second way is to permanently remove all the flags under `// Checks` comment.
* It's possible to face a situation when you will get an error when trying to use some default object like `window`, `document`, `Promise` or some operators. It means you have to include library's build-in typings to your `tsconfig`. [List of allowed libraries](https://www.typescriptlang.org/tsconfig#lib) you can find here. Current example looks like that:
```json
{
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"build:dev": "ts-node --project ./tsconfig-dev.json ./tasks/run-build.ts -d",
"start": "node ./dist/index.js",
"test": "echo \"Error: no test specified\" && exit 1",
"release": "NODE_ENV=production ts-node ./tasks/release.ts"
"release": "ts-node ./tasks/release.ts"
},
"devDependencies": {
"@types/ejs": "^3.0.4",
Expand Down
4 changes: 2 additions & 2 deletions scaffold/_tsconfig/tsconfig-advanced.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
"declaration": true,
"declarationDir": "./dist/__types__",
"resolveJsonModule": true,

// Production specific
"removeComments": true,
"sourceMap": false,
"watch": false,

// Checks
"allowUnreachableCode": false,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
Expand Down
2 changes: 1 addition & 1 deletion scaffold/_tsconfig/tsconfig-base.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"declaration": true,
"declarationDir": "./dist/__types__",
"resolveJsonModule": true,

// Production specific
"removeComments": true,
"sourceMap": false,
"watch": false
Expand Down
3 changes: 2 additions & 1 deletion scaffold/_tsconfig/tsconfig-dev.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
// Development specific
"removeComments": false,
"sourceMap": true,
"watch": true,

// Checks
"noUnusedLocals": false,
"allowUnreachableCode": true
}
Expand Down
4 changes: 2 additions & 2 deletions scaffold/package.json.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
"build": "ts-node ./tasks/run-build.ts",
"build:dev": "ts-node --project ./tsconfig-dev.json ./tasks/run-build.ts -d",
"start": "node ./dist/index.js",
"test": "echo \"Error: no test specified\" && exit 1",
"release": "NODE_ENV=production ts-node ./tasks/release.ts"
"test": "echo \"Error: no test specified\" && exit 1"<% if (isNpmPackage) {%>,
"release": "NODE_ENV=production ts-node ./tasks/release.ts"<% } %>
},<% if (author) { %>
"author": {
"name": "<%= author %>"
Expand Down
21 changes: 18 additions & 3 deletions scaffold/tasks/build.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
import fs from 'fs'
import { run } from './process'
import tsconfig from '../tsconfig.json'

function readOutDirNameFromConfig (): string | null {
const commentsRegExp = new RegExp(/\/\/(.*)/, 'g')
let configData = fs.readFileSync('./tsconfig.json', 'utf8')
configData = configData.replace(commentsRegExp, '')
const config = JSON.parse(configData)

if (typeof config === 'object' && config?.compilerOptions?.outDir != null) {
return String(config.compilerOptions.outDir)
} else {
return null
}
}

export async function build (mode: 'production' | 'development'): Promise<string> {
const distPath = tsconfig.compilerOptions.outDir
let buildCommand = 'tsc'
const distPath = readOutDirNameFromConfig()
if (distPath == null) {
throw new Error('Option "compilerOptions.outDir" is not specified in tsconfig.json.')
}

let buildCommand = 'tsc'
if (mode === 'development') {
buildCommand += ' --project ./tsconfig-dev.json'
}
Expand Down
1 change: 1 addition & 0 deletions src/app-configurator/AppConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import License from './License'
export default interface AppConfig {
[ConfigProperty.Author]: string
[ConfigProperty.Name]: string
[ConfigProperty.NpmPackage]: boolean
[ConfigProperty.License]: License | null
[ConfigProperty.TsAdvanced]: boolean
}
15 changes: 15 additions & 0 deletions src/app-configurator/AppConfigurator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,21 @@ export default class AppConfigurator {
return true
}
},
{
type: 'list',
name: ConfigProperty.NpmPackage,
message: 'Project type:',
choices: [
{
name: 'npm package',
value: true
},
{
name: 'some app',
value: false
}
]
},
{
type: 'list',
name: ConfigProperty.License,
Expand Down
1 change: 1 addition & 0 deletions src/app-configurator/ConfigProperty.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
enum ConfigProperty {
Author = 'author',
Name = 'name',
NpmPackage = 'isNpmPackage',
License = 'license',
TsAdvanced = 'tsAdvanced'
}
Expand Down
80 changes: 55 additions & 25 deletions src/app-generator/AppGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import fs from 'fs'
import path from 'path'
import ejs from 'ejs'
import AppConfig from '../app-configurator/AppConfig'
import FileCopyParams from './FileCopyParams'

class AppGenerator {
private readonly config: AppConfig
Expand All @@ -21,44 +22,59 @@ class AppGenerator {
if (this.config.license !== null) {
const licenseFile = this.config.license.name.replace(/"/g, '')

await this.copyFile(
`_licenses/${licenseFile}`,
{
await this.copyFile({
from: `_licenses/${licenseFile}`,
to: 'LICENSE',
data: {
year: new Date().getFullYear(),
author: this.config.author
},
'LICENSE'
)
}
})
}
if (this.config.tsAdvanced) {
await this.copyFile('_tsconfig/tsconfig-advanced.json', {}, 'tsconfig.json')
await this.copyFile({
from: '_tsconfig/tsconfig-advanced.json',
to: 'tsconfig.json'
})
} else {
await this.copyFile('_tsconfig/tsconfig-base.json', {}, 'tsconfig.json')
await this.copyFile({
from: '_tsconfig/tsconfig-base.json',
to: 'tsconfig.json'
})
}
await this.copyFile('_tsconfig/tsconfig-dev.json', {}, 'tsconfig-dev.json')
await this.copyFile('tasks/build.ts')
await this.copyFile({
from: '_tsconfig/tsconfig-dev.json',
to: 'tsconfig-dev.json'
})
await this.copyFile('tasks/process.ts')
await this.copyFile('tasks/release.ts')
await this.copyFile('tasks/build.ts')
await this.copyFile('tasks/run-build.ts')
await this.copyFile('tasks/run-release.ts')
await this.copyFile('_gitignore', {}, '.gitignore')
await this.copyFile(
'package.json.ejs',
{
if (this.config.isNpmPackage) {
await this.copyFile('tasks/release.ts')
await this.copyFile('tasks/run-release.ts')
}
await this.copyFile({
from: '_gitignore',
to: '.gitignore'
})
await this.copyFile({
from: 'package.json.ejs',
data: {
name: this.config.name,
author: this.config.author,
license: this.config.license
license: this.config.license,
isNpmPackage: this.config.isNpmPackage
}
)
})
await this.copyFile('README.md')
await this.copyFile('src/Greeter.ts')
await this.copyFile(
'src/index.ts.ejs',
{
await this.copyFile({
from: 'src/index.ts.ejs',
data: {
author: this.config.author,
projectName: this.config.name
}
)
})
} catch (error) {
await this.removeAppFolder()
throw error
Expand All @@ -73,11 +89,25 @@ class AppGenerator {
fs.rmdirSync(this.config.name, { recursive: true })
}

private async copyFile (tplPath: string, tplData?: object, destPath?: string): Promise<string> {
const copyDestPath: string = this.removeEjsExtensionFromPath(destPath ?? tplPath)
private async copyFile (fileOptions: FileCopyParams): Promise<string>
private async copyFile (fileOptions: string): Promise<string>
private async copyFile (fileOptions: string | FileCopyParams): Promise<string> {
let from: string
let to: string | undefined
let data: Record<string, unknown> | undefined

if (typeof fileOptions === 'object') {
from = fileOptions.from
to = fileOptions.to
data = fileOptions.data
} else {
from = fileOptions
}

const copyDestPath: string = this.removeEjsExtensionFromPath(to ?? from)
const absoluteCopyDestPath: string = path.join(this.projectPath, copyDestPath)
const absoluteCopyDestDir: string = path.dirname(absoluteCopyDestPath)
const content: string = await ejs.renderFile(path.join(this.templatesPath, tplPath), tplData ?? {})
const content: string = await ejs.renderFile(path.join(this.templatesPath, from), data ?? {})
const isDestFolderExists: boolean = fs.existsSync(absoluteCopyDestDir)

if (!isDestFolderExists) {
Expand Down
5 changes: 5 additions & 0 deletions src/app-generator/FileCopyParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default interface FileCopyParams {
from: string
to?: string
data?: Record<string, unknown>
}
21 changes: 18 additions & 3 deletions tasks/build.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
import fs from 'fs'
import { run } from './process'
import tsconfig from '../tsconfig.json'

function readOutDirNameFromConfig (): string | null {
const commentsRegExp = new RegExp(/\/\/(.*)/, 'g')
let configData = fs.readFileSync('./tsconfig.json', 'utf8')
configData = configData.replace(commentsRegExp, '')
const config = JSON.parse(configData)

if (typeof config === 'object' && config?.compilerOptions?.outDir != null) {
return String(config.compilerOptions.outDir)
} else {
return null
}
}

export async function build (mode: 'production' | 'development'): Promise<string> {
const distPath = tsconfig.compilerOptions.outDir
let buildCommand = 'tsc'
const distPath = readOutDirNameFromConfig()
if (distPath == null) {
throw new Error('Option "compilerOptions.outDir" is not specified in tsconfig.json.')
}

let buildCommand = 'tsc'
if (mode === 'development') {
buildCommand += ' --project ./tsconfig-dev.json'
}
Expand Down
3 changes: 2 additions & 1 deletion tsconfig-dev.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
// Development specific
"removeComments": false,
"sourceMap": true,
"watch": true,

// Checks
"noUnusedLocals": false,
"allowUnreachableCode": true
}
Expand Down
4 changes: 2 additions & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@
"ES6",
"DOM"
],

// Production specific
"removeComments": true,
"sourceMap": false,
"watch": false,

// Checks
"allowUnreachableCode": false,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
Expand Down

0 comments on commit a2ed5e4

Please sign in to comment.