Skip to content
This repository has been archived by the owner on Feb 5, 2023. It is now read-only.

Commit

Permalink
feat: Add base files
Browse files Browse the repository at this point in the history
  • Loading branch information
ffflorian committed Jun 12, 2019
1 parent fb155a1 commit 9e613f2
Show file tree
Hide file tree
Showing 17 changed files with 5,967 additions and 21 deletions.
9 changes: 9 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
root = true

[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
1 change: 1 addition & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ko_fi: ffflorian
61 changes: 61 additions & 0 deletions .github/main.workflow
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
workflow "Build, lint and test" {
on = "push"
resolves = [
"Build project",
"Lint project",
"Publish project"
]
}

action "Don't skip CI" {
uses = "ffflorian/actions/skip-ci-check@v1.0.0"
}

action "Install dependencies" {
uses = "ffflorian/actions/git-node@v1.0.0"
needs = "Don't skip CI"
runs = "yarn"
}

action "Lint project" {
uses = "ffflorian/actions/git-node@v1.0.0"
needs = "Install dependencies"
runs = "yarn"
args = "lint"
}

action "Build project" {
uses = "ffflorian/actions/git-node@v1.0.0"
needs = "Install dependencies"
runs = "yarn"
args = "dist"
}

action "Check for master branch" {
uses = "actions/bin/filter@master"
needs = [
"Build project",
"Lint project",
]
args = "branch master"
}

action "Don't publish dependency updates" {
uses = "ffflorian/actions/last_commit@v1.0.0"
needs = "Check for master branch"
args = "^(?!chore\\(deps)"
}

action "Publish project" {
uses = "ffflorian/actions/git-node@v1.0.0"
needs = "Don't publish dependency updates"
env = {
GIT_AUTHOR_NAME = "ffflobot"
GIT_AUTHOR_EMAIL = "ffflobot@users.noreply.github.com"
GIT_COMMITTER_NAME = "ffflobot"
GIT_COMMITTER_EMAIL = "ffflobot@users.noreply.github.com"
}
runs = "yarn"
args = "release"
secrets = ["GH_TOKEN", "NPM_TOKEN"]
}
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules/
dist/
*.log
spec/.temp
2 changes: 2 additions & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
registry=https://registry.npmjs.org/
save-exact=true
3 changes: 3 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
dist
node_modules
package.json
22 changes: 22 additions & 0 deletions .releaserc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
"@semantic-release/changelog",
"@semantic-release/npm",
[
"@semantic-release/github",
{
"releasedLabels": false,
"successComment": false
}
],
[
"@semantic-release/git",
{
"assets": ["package.json", "CHANGELOG.md"],
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}
]
]
}
21 changes: 0 additions & 21 deletions LICENSE

This file was deleted.

36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# publisher [![Build Status](https://action-badges.now.sh/ffflorian/publisher)](https://github.com/ffflorian/publisher/actions/) [![npm version](https://img.shields.io/npm/v/@ffflorian/publisher.svg?style=flat)](https://www.npmjs.com/package/@ffflorian/publisher) [![Dependabot Status](https://api.dependabot.com/badges/status?host=github&repo=ffflorian/publisher)](https://dependabot.com)

Publish your project without the dist directory.

## Description

Here is what it does:

1. Re-build your project
2. Copy your dist files into a temporary directory
3. Publish your project from the temporary directory

## Installation

```
yarn add @ffflorian/publisher
```

### CLI Usage

```
Usage: cli.ts [options] <dir>
Publish your project without the dist directory
Options:
-V, --version output the version number
-c, --yarn Use yarn for publishing (default: false)
-o, --omit <dir> Which directory to omit (default: "dist")
-n, --no-publish Do not publish (default: false)
-h, --help output usage information
```

### API Usage

See [`cli.ts`](./src/cli.ts).
79 changes: 79 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
{
"author": "Florian Keller <github@floriankeller.de>",
"bin": {
"publisher": "dist/cli.js"
},
"dependencies": {
"commander": "2.20.0",
"commander-remaining-args": "1.2.0",
"fs-extra": "8.0.1",
"logdown": "3.2.8",
"npm-packlist": "1.4.1"
},
"description": "Publish your project without the dist directory",
"devDependencies": {
"@ffflorian/prettier-config": "0.0.5",
"@ffflorian/tslint-config": "0.2.2",
"@semantic-release/changelog": "3.0.2",
"@semantic-release/git": "7.0.8",
"@types/fs-extra": "7.0.0",
"@types/jasmine": "3.3.13",
"@types/node": "~12",
"@types/npm-packlist": "1.1.1",
"husky": "2.4.1",
"jasmine": "3.4.0",
"lint-staged": "8.2.0",
"prettier": "1.18.2",
"rimraf": "2.6.3",
"semantic-release": "15.13.12",
"ts-node": "8.2.0",
"tslint": "5.17.0",
"tslint-config-prettier": "1.18.0",
"tslint-plugin-prettier": "2.0.1",
"typescript": "3.5.1"
},
"files": [
"dist"
],
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"keywords": [
"cli",
"typescript"
],
"license": "MIT",
"lint-staged": {
"*.{json,md}": [
"prettier --write",
"git add"
],
"*.ts": [
"tslint --project tsconfig.json --fix",
"git add"
]
},
"main": "dist/index.js",
"name": "publisher",
"prettier": "@ffflorian/prettier-config",
"repository": "https://github.com/ffflorian/publisher.git",
"scripts": {
"build": "tsc",
"clean": "rimraf dist",
"dist": "yarn clean && yarn build",
"fix": "yarn fix:other && yarn fix:ts",
"fix:other": "yarn prettier --write",
"fix:ts": "yarn lint:ts --fix",
"lint": "yarn lint:ts && yarn lint:other",
"lint:other": "yarn prettier --list-different",
"lint:ts": "tslint --project tsconfig.json",
"prettier": "prettier \"*.{json,md}\"",
"publish": "node dist/cli.js",
"release": "semantic-release",
"start": "ts-node src/cli.ts",
"test": "exit 0"
},
"version": "0.0.1"
}
147 changes: 147 additions & 0 deletions src/Publisher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import {exec} from 'child_process';
import * as os from 'os';
import * as path from 'path';
import {promisify} from 'util';

import * as fs from 'fs-extra';
import * as logdown from 'logdown';
import * as packlist from 'npm-packlist';

const execAsync = promisify(exec);

export interface PublishOptions {
/** Which directory to omit (e.g. to move dist/main.js => main.js, use `dist`) */
dirToOmit: string;
packageDir: string;
/** Arguments to forward to npm or yarn */
publishArguments?: string[];
/** Use yarn for publishing */
useYarn?: boolean;
}

type FilesInOmittedDir = Array<{fileName: string; replacedFilename: string}>;

interface Categorized {
filesInOmittedDir: FilesInOmittedDir;
normalFiles: string[];
}

export class Publisher {
private readonly options: PublishOptions;
private readonly logger: logdown.Logger;
private readonly packageDir: string;
private readonly dirToOmit: string;
private readonly dirToOmitRegex: RegExp;

constructor(options: PublishOptions) {
this.options = options;
this.logger = logdown('publisher', {
logger: console,
markdown: false,
});
this.logger.state.isEnabled = true;

this.packageDir = path.resolve(this.options.packageDir);
this.dirToOmit = this.cleanDirName(this.options.dirToOmit);
this.dirToOmitRegex = new RegExp(`${this.dirToOmit}[\\/]`);
}

private cleanDirName(dirName: string): string {
const separatorRegex = new RegExp('[\\/]*([^\\/]+)[\\/]*', 'g');
const cleanName = dirName.trim().replace(separatorRegex, '$1');
if (!cleanName) {
throw new Error(`Invalid omit dir "${dirName}" specified`);
}
return cleanName;
}

private createTempDir(): Promise<string> {
return fs.mkdtemp(path.join(os.tmpdir(), 'publisher-'));
}

private async cleanPackageJson(filePath: string, filesInOmittedDir: FilesInOmittedDir): Promise<void> {
const packageJson = await fs.readJSON(filePath);
packageJson.files = packageJson.files.map((fileName: string) => fileName.replace(this.dirToOmitRegex, ''));
packageJson.files = packageJson.files.concat(filesInOmittedDir.map(({replacedFilename}) => replacedFilename));
packageJson.files = packageJson.files.filter((fileName: string) => fileName !== this.dirToOmit);

if (typeof packageJson.bin === 'string') {
packageJson.bin = packageJson.bin.replace(this.dirToOmitRegex, '');
} else if (typeof packageJson.bin === 'object') {
for (const binName of Object.keys(packageJson.bin)) {
packageJson.bin[binName] = packageJson.bin[binName].replace(this.dirToOmitRegex, '');
}
}

if (packageJson.main) {
packageJson.main = packageJson.main.replace(this.dirToOmitRegex, '');
}

const packageJsonString = `${JSON.stringify(packageJson, null, 2)}\n`;
await fs.writeFile(filePath, packageJsonString, 'utf-8');
}

async publish(tempDir: string): Promise<void> {
this.logger.info(`Publishing package in "${this.packageDir}" ...`);

const executor = this.options.useYarn ? 'yarn' : 'npm';
const command = `${executor} publish "${tempDir}" ${this.options.publishArguments}`.trim();

this.logger.info(`Running "${command}" ...`);

const {stderr, stdout} = await execAsync(command);

if (stderr) {
throw new Error(stderr);
}

this.logger.info(stdout);

await fs.remove(tempDir);
}

async build(): Promise<string | void> {
const files = await packlist({path: this.packageDir});

this.logger.info('Got files', files);

if (!files.length) {
this.logger.info('No files to publish');
return;
}

if (!files.includes('package.json')) {
throw new Error(`Files don't include a "package.json" file`);
}

const {normalFiles, filesInOmittedDir} = files.reduce(
(result: Categorized, fileName: string) => {
if (this.dirToOmitRegex.test(fileName)) {
const replacedFilename = fileName.replace(this.dirToOmitRegex, '');
result.filesInOmittedDir.push({fileName, replacedFilename});
} else {
result.normalFiles.push(fileName);
}
return result;
},
{normalFiles: [], filesInOmittedDir: []}
);

const tempDir = await this.createTempDir();

for (const file of normalFiles) {
await fs.copy(path.join(this.packageDir, file), path.join(tempDir, file), {overwrite: true, recursive: true});
}

for (const {fileName, replacedFilename} of filesInOmittedDir) {
await fs.copy(path.join(this.packageDir, fileName), path.join(tempDir, replacedFilename), {
overwrite: true,
recursive: true,
});
}

await this.cleanPackageJson(path.join(tempDir, 'package.json'), filesInOmittedDir);

return tempDir;
}
}
Loading

0 comments on commit 9e613f2

Please sign in to comment.