Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
ghiscoding committed Dec 7, 2024
2 parents ee1d9cf + 189a97c commit b150ae3
Show file tree
Hide file tree
Showing 9 changed files with 948 additions and 7 deletions.
25 changes: 20 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,12 +154,27 @@ pnpm run cypress
pnpm run cypress:ci
```


## Sponsors

<div>
<img class="circle avatar-user" src="https://avatars.githubusercontent.com/u/48218815?s=52&amp;v=4" width="45" height="45" alt="@kevinburkett" />
<a href="/kevinburkett" class="Link">
<span class="wb-break-word ml-2">kevinburkett</span>
</a>
<span>
<a href="https://github.com/wundergraph" class="Link" title="Wundergraph" target="_blank"><img src="https://avatars.githubusercontent.com/u/64281914" width="50" height="50" valign="middle" /></a>
</span>
&nbsp;
<span>
<a href="https://github.com/johnsoncodehk" class="Link" title="johnsoncodehk (Volar)" target="_blank"><img src="https://avatars.githubusercontent.com/u/16279759" width="50" height="50" valign="middle" /></a>
</span>
&nbsp;
<span>
<a href="https://github.com/kevinburkett" class="Link" title="kevinburkett" target="_blank"><img class="circle avatar-user" src="https://avatars.githubusercontent.com/u/48218815?s=52&amp;v=4" width="45" height="45" valign="middle" /></a>
</span>
&nbsp;
<span>
<a href="https://github.com/anton-gustafsson" class="Link" title="anton-gustafsson" target="_blank"><img src="https://avatars.githubusercontent.com/u/22906905?s=52&v=4" width="50" height="50" valign="middle" /></a>
</span>
&nbsp;
<span>
<a href="https://github.com/gibson552" class="Link" title="gibson552" target="_blank"><img src="https://avatars.githubusercontent.com/u/84058359?s=52&v=4" width="50" height="50" valign="middle" /></a>
</span>
</div>

12 changes: 10 additions & 2 deletions frameworks/slickgrid-vue/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@
"vue:dev:init": "vite build",
"vue:build": "pnpm clean && vue-tsc --p ./tsconfig.app.json && vite build --sourcemap && pnpm clone:dts",
"clone:dts": "node clone-dts.mjs",
"preview:release": "node ./scripts/release.mjs --dry-run --skip-checks",
"release": "node ./scripts/release.mjs",
"type-check": "vue-tsc --build --force"
},
"dependencies": {
Expand All @@ -77,11 +79,17 @@
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.2.1",
"conventional-changelog": "^6.0.0",
"sass": "^1.81.0",
"semver": "^7.6.3",
"strong-log-transformer": "^2.1.0",
"tinyexec": "^0.3.1",
"tinyrainbow": "^1.2.0",
"typescript": "~5.6.2",
"vite": "^6.0.1",
"vite-plugin-dts": "^4.3.0",
"vue": "^3.5.13",
"vue-tsc": "^2.1.10"
"vue-tsc": "^2.1.10",
"yargs": "^17.7.2"
}
}
}
66 changes: 66 additions & 0 deletions frameworks/slickgrid-vue/scripts/changelog.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import conventionalChangelog from 'conventional-changelog';
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
import path from 'node:path';

const projectRootLocation = process.cwd();
const EOL = '\n';
const BLANK_LINE = EOL + EOL;
const CHANGELOG_HEADER = [
'# Change Log',
'All notable changes to this project will be documented in this file.',
'See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.'
].join(EOL);

/**
* Insert/Update "CHANGELOG.md" with conventional commits since last tagged version
* @param { { infile: String, preset: String, tagPrefix: String } } args
* @param {String} newVersion
* @returns
*/
export function updateChangelog(args, newVersion) {
const default_args = { preset: 'angular' };
args = Object.assign({}, default_args, args);
const { infile, preset, tagPrefix } = args;

return new Promise((resolve, reject) => {
let content = '';
let oldContent = '';

// read changelog.md if it exist or else we'll create it
const changelogLocation = path.resolve(projectRootLocation, infile);
const fileExist = existsSync(changelogLocation);
if (fileExist) {
oldContent = readFileSync(path.resolve(projectRootLocation, infile), 'utf8');
}

// find the position of the last release and remove header since we'll append it back on top
let oldContentWithoutHeader = oldContent;
if (oldContent.includes(CHANGELOG_HEADER)) {
oldContentWithoutHeader = oldContent.substring(CHANGELOG_HEADER.length);
}

const context = { version: newVersion };
const changelogStream = conventionalChangelog(
{ preset, tagPrefix },
context,
{ merges: null, path: args.path }
).on('error', (err) => {
return reject(err);
});

changelogStream.on('data', (buffer) => {
content += buffer.toString();
});

changelogStream.on('end', () => {
const newContent = [CHANGELOG_HEADER, content, oldContentWithoutHeader]
.join(BLANK_LINE)
.trim()
.replace(/[\r\n]{2,}/gm, '\n\n'); // conventional-changelog adds way too many extra line breaks, let's remove a few of them

writeFileSync(changelogLocation, newContent, 'utf8');

return resolve({ location: changelogLocation, newEntry: content });
});
});
}
176 changes: 176 additions & 0 deletions frameworks/slickgrid-vue/scripts/child-process.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import os from 'node:os';
import logTransformer from 'strong-log-transformer';
import { x } from 'tinyexec';
import c from 'tinyrainbow';

// bookkeeping for spawned processes
const children = new Set();

/**
* Execute a command asynchronously, piping stdio by default.
* @param {String} command - shell command
* @param {String[]} execArgs - shell command arguments
* @param {import("tinyexec").Options} [opts] - tinyexec node options
* @param {Boolean} [cmdDryRun]
*/
export function execAsyncPiped(command, execArgs, execOpts, cmdDryRun) {
const options = {
nodeOptions: {
...execOpts,
stdio: ['pipe'],
}
};
const spawned = spawnProcess(command, execArgs, options, cmdDryRun);

return cmdDryRun ? Promise.resolve() : wrapError(spawned);
}

/**
* Execute a command synchronously.
* @param {String} command - shell command
* @param {String[]} args - shell command arguments
* @param {import("tinyexec").Options} [opts] - tinyexec options
* @param {Boolean} [cmdDryRun] - dry-run flag
*/
export async function execAsync(command, args, opts, cmdDryRun = false) {
return cmdDryRun
? logExecDryRunCommand(command, args)
: (await x('git', args, opts)).stdout.trim();
}

/**
* Log the exec command without actually executing the actual command
* @param {String} command - shell command
* @param {String[]} args - shell command arguments
* @returns {String} output
*/
export function logExecDryRunCommand(command, args) {
const argStr = (Array.isArray(args) ? args.join(' ') : args) ?? '';

const cmdList = [];
for (const cmd of [command, argStr]) {
cmdList.push(Array.isArray(cmd) ? cmd.join(' ') : cmd);
}

console.info(c.magenta(c.bold('[dry-run] >')), cmdList.join(' '));
return '';
}

/**
* @param {String} command - shell command
* @param {String[]} args - shell command arguments
* @param {import("tinyexec").Options} execOpts - tinyexec options
* @returns {Promise<any>}
*/
export async function spawnProcess(
command,
args,
execOpts,
cmdDryRun = false
) {
if (cmdDryRun) {
return logExecDryRunCommand(command, args);
}
const child = x(command, args, execOpts);
const drain = (_code, signal) => {
children.delete(child);

// don't run repeatedly if this is the error event
if (signal === undefined) {
child.process.removeListener('exit', drain);
}
};

child.process.once('exit', drain);
child.process.once('error', drain);
children.add(child);

return child;
}

/**
* Spawn a command asynchronously, streaming stdio with optional prefix.
* @param {String} command
* @param {String[]} args
* @param {import("tinyexec").Options} [opts]
* @param {String} [prefix]
* @param {Boolean} [cmdDryRun=false]
*/
// istanbul ignore next
export function spawnStreaming(
command,
args,
opts,
prefix,
cmdDryRun = false
) {
const options = {
...opts,
nodeOptions: {
stdio: ['ignore', 'pipe'],
}
};

if (cmdDryRun) {
return logExecDryRunCommand(command, args);
}
const spawned = spawnProcess(command, args, options, cmdDryRun);

const stdoutOpts = {};
const stderrOpts = {}; // mergeMultiline causes escaped newlines :P

if (prefix) {
const color = c['magenta'];
stdoutOpts.tag = `${color.bold(prefix)}:`;
stderrOpts.tag = `${color(prefix)}:`;
}

// Avoid 'Possible EventEmitter memory leak detected' warning due to piped stdio
if (children.size > process.stdout.listenerCount('close')) {
process.stdout.setMaxListeners(children.size);
process.stderr.setMaxListeners(children.size);
}

spawned.stdout?.pipe(logTransformer(stdoutOpts)).pipe(process.stdout);
spawned.stderr?.pipe(logTransformer(stderrOpts)).pipe(process.stderr);

return wrapError(spawned);
}

// --
// private functions

/**
* Return the exitCode when possible or else throw an error
* @param {*} result
* @returns
*/
function getExitCode(result) {
// https://nodejs.org/docs/latest-v6.x/api/child_process.html#child_process_event_close
if (typeof result.code === 'number' || typeof result.exitCode === 'number') {
return result.code ?? result.exitCode;
}

// https://nodejs.org/docs/latest-v6.x/api/errors.html#errors_error_code
// istanbul ignore else
if (typeof result.code === 'string' || typeof result.exitCode === 'string') {
return os.constants.errno[result.code ?? result.exitCode];
}

// istanbul ignore next: extremely weird
throw new Error(`Received unexpected exit code value ${JSON.stringify(result.code ?? result.exitCode)}`);
}

/**
* Spawn a command asynchronously, _always_ inheriting stdio.
* @param {import("tinyexec").Output} spawned
*/
function wrapError(spawned) {
return spawned.catch((err) => {
// ensure exit code is always a number
err.exitCode = getExitCode(err);

console.error('SPAWN PROCESS ERROR');
throw err;
});
}
58 changes: 58 additions & 0 deletions frameworks/slickgrid-vue/scripts/fs-utils.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* Copy some fs-extra util implementations
* https://github.com/jprichardson/node-fs-extra
*/

import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
import { dirname } from 'node:path';

export function outputFileSync(file, ...args) {
const dir = dirname(file);
if (!existsSync(dir)) {
mkdirSync(dir, { recursive: true });
}

writeFileSync(file, ...args);
}

export function readJSONSync(file, options = {}) {
if (typeof options === 'string') {
options = { encoding: options };
}

const shouldThrow = 'throws' in options ? options.throws : true;

try {
let content = readFileSync(file, options);
content = stripBom(content);
return JSON.parse(content, options.reviver);
} catch (err) {
if (shouldThrow) {
err.message = `${file}: ${err.message}`;
throw err;
} else {
return null;
}
}
}

export function stringify(obj, { EOL = '\n', finalEOL = true, replacer = null, spaces } = {}) {
const EOF = finalEOL ? EOL : '';
const str = JSON.stringify(obj, replacer, spaces);

return str.replace(/\n/g, EOL) + EOF;
}

export function stripBom(content) {
// we do this because JSON.parse would convert it to a utf8 string if encoding wasn't specified
if (Buffer.isBuffer(content)) {
content = content.toString('utf8');
}
return content.replace(/^\uFEFF/, '');
}

export function writeJsonSync(file, obj, options = {}) {
const str = stringify(obj, options);
// not sure if fs.writeFileSync returns anything, but just in case
return writeFileSync(file, str, options);
}
Loading

0 comments on commit b150ae3

Please sign in to comment.