mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-04-10 19:33:37 +08:00
fix: narrow unicode cleanup scope
This commit is contained in:
@@ -7,7 +7,9 @@
|
|||||||
# ./install.sh # Install to current directory
|
# ./install.sh # Install to current directory
|
||||||
# ./install.sh /path/to/dir # Install to specific directory
|
# ./install.sh /path/to/dir # Install to specific directory
|
||||||
# ./install.sh ~ # Install globally to ~/.kiro/
|
# ./install.sh ~ # Install globally to ~/.kiro/
|
||||||
# set -euo pipefail
|
#
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
# When globs match nothing, expand to empty list instead of the literal pattern
|
# When globs match nothing, expand to empty list instead of the literal pattern
|
||||||
shopt -s nullglob
|
shopt -s nullglob
|
||||||
|
|||||||
@@ -9,7 +9,9 @@
|
|||||||
#
|
#
|
||||||
# Environment:
|
# Environment:
|
||||||
# TRAE_ENV=cn # Force use .trae-cn directory
|
# TRAE_ENV=cn # Force use .trae-cn directory
|
||||||
# set -euo pipefail
|
#
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
# When globs match nothing, expand to empty list instead of the literal pattern
|
# When globs match nothing, expand to empty list instead of the literal pattern
|
||||||
shopt -s nullglob
|
shopt -s nullglob
|
||||||
|
|||||||
@@ -9,7 +9,9 @@
|
|||||||
#
|
#
|
||||||
# Environment:
|
# Environment:
|
||||||
# TRAE_ENV=cn # Force use .trae-cn directory
|
# TRAE_ENV=cn # Force use .trae-cn directory
|
||||||
# set -euo pipefail
|
#
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
# Resolve the directory where this script lives
|
# Resolve the directory where this script lives
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
|||||||
@@ -76,9 +76,9 @@ function parseReadmeExpectations(readmeContent) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const tablePatterns = [
|
const tablePatterns = [
|
||||||
{ category: 'agents', regex: /\|\s*(?:\*\*)?Agents(?:\*\*)?\s*\|\s*PASS:\s*(\d+)\s+agents\s*\|/i, source: 'README.md comparison table' },
|
{ category: 'agents', regex: /\|\s*(?:\*\*)?Agents(?:\*\*)?\s*\|\s*(?:(?:PASS:|\u2705)\s*)?(\d+)\s+agents\s*\|/i, source: 'README.md comparison table' },
|
||||||
{ category: 'commands', regex: /\|\s*(?:\*\*)?Commands(?:\*\*)?\s*\|\s*PASS:\s*(\d+)\s+commands\s*\|/i, source: 'README.md comparison table' },
|
{ category: 'commands', regex: /\|\s*(?:\*\*)?Commands(?:\*\*)?\s*\|\s*(?:(?:PASS:|\u2705)\s*)?(\d+)\s+commands\s*\|/i, source: 'README.md comparison table' },
|
||||||
{ category: 'skills', regex: /\|\s*(?:\*\*)?Skills(?:\*\*)?\s*\|\s*PASS:\s*(\d+)\s+skills\s*\|/i, source: 'README.md comparison table' }
|
{ category: 'skills', regex: /\|\s*(?:\*\*)?Skills(?:\*\*)?\s*\|\s*(?:(?:PASS:|\u2705)\s*)?(\d+)\s+skills\s*\|/i, source: 'README.md comparison table' }
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const pattern of tablePatterns) {
|
for (const pattern of tablePatterns) {
|
||||||
|
|||||||
@@ -39,6 +39,12 @@ const textExtensions = new Set([
|
|||||||
'.rs',
|
'.rs',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const writableExtensions = new Set([
|
||||||
|
'.md',
|
||||||
|
'.mdx',
|
||||||
|
'.txt',
|
||||||
|
]);
|
||||||
|
|
||||||
const writeModeSkip = new Set([
|
const writeModeSkip = new Set([
|
||||||
path.normalize('scripts/ci/check-unicode-safety.js'),
|
path.normalize('scripts/ci/check-unicode-safety.js'),
|
||||||
path.normalize('tests/scripts/check-unicode-safety.test.js'),
|
path.normalize('tests/scripts/check-unicode-safety.test.js'),
|
||||||
@@ -47,6 +53,11 @@ const writeModeSkip = new Set([
|
|||||||
const dangerousInvisibleRe =
|
const dangerousInvisibleRe =
|
||||||
/[\u200B-\u200D\u2060\uFEFF\u202A-\u202E\u2066-\u2069\uFE00-\uFE0F\u{E0100}-\u{E01EF}]/gu;
|
/[\u200B-\u200D\u2060\uFEFF\u202A-\u202E\u2066-\u2069\uFE00-\uFE0F\u{E0100}-\u{E01EF}]/gu;
|
||||||
const emojiRe = /[\p{Extended_Pictographic}\p{Regional_Indicator}]/gu;
|
const emojiRe = /[\p{Extended_Pictographic}\p{Regional_Indicator}]/gu;
|
||||||
|
const allowedSymbolCodePoints = new Set([
|
||||||
|
0x00A9,
|
||||||
|
0x00AE,
|
||||||
|
0x2122,
|
||||||
|
]);
|
||||||
|
|
||||||
const targetedReplacements = [
|
const targetedReplacements = [
|
||||||
[new RegExp(`${String.fromCodePoint(0x26A0)}(?:\\uFE0F)?`, 'gu'), 'WARNING:'],
|
[new RegExp(`${String.fromCodePoint(0x26A0)}(?:\\uFE0F)?`, 'gu'), 'WARNING:'],
|
||||||
@@ -64,6 +75,10 @@ function isTextFile(filePath) {
|
|||||||
return textExtensions.has(path.extname(filePath).toLowerCase());
|
return textExtensions.has(path.extname(filePath).toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function canAutoWrite(relativePath) {
|
||||||
|
return writableExtensions.has(path.extname(relativePath).toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
function listFiles(dirPath) {
|
function listFiles(dirPath) {
|
||||||
const results = [];
|
const results = [];
|
||||||
for (const entry of fs.readdirSync(dirPath, { withFileTypes: true })) {
|
for (const entry of fs.readdirSync(dirPath, { withFileTypes: true })) {
|
||||||
@@ -87,6 +102,10 @@ function lineAndColumn(text, index) {
|
|||||||
return { line, column };
|
return { line, column };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isAllowedEmojiLikeSymbol(char) {
|
||||||
|
return allowedSymbolCodePoints.has(char.codePointAt(0));
|
||||||
|
}
|
||||||
|
|
||||||
function sanitizeText(text) {
|
function sanitizeText(text) {
|
||||||
let next = text;
|
let next = text;
|
||||||
next = next.replace(dangerousInvisibleRe, '');
|
next = next.replace(dangerousInvisibleRe, '');
|
||||||
@@ -95,7 +114,7 @@ function sanitizeText(text) {
|
|||||||
next = next.replace(pattern, replacement);
|
next = next.replace(pattern, replacement);
|
||||||
}
|
}
|
||||||
|
|
||||||
next = next.replace(emojiRe, '');
|
next = next.replace(emojiRe, match => (isAllowedEmojiLikeSymbol(match) ? match : ''));
|
||||||
next = next.replace(/^ +(?=\*\*)/gm, '');
|
next = next.replace(/^ +(?=\*\*)/gm, '');
|
||||||
next = next.replace(/^(\*\*)\s+/gm, '$1');
|
next = next.replace(/^(\*\*)\s+/gm, '$1');
|
||||||
next = next.replace(/^(#+)\s{2,}/gm, '$1 ');
|
next = next.replace(/^(#+)\s{2,}/gm, '$1 ');
|
||||||
@@ -111,6 +130,9 @@ function collectMatches(text, regex, kind) {
|
|||||||
const matches = [];
|
const matches = [];
|
||||||
for (const match of text.matchAll(regex)) {
|
for (const match of text.matchAll(regex)) {
|
||||||
const char = match[0];
|
const char = match[0];
|
||||||
|
if (kind === 'emoji' && isAllowedEmojiLikeSymbol(char)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const index = match.index ?? 0;
|
const index = match.index ?? 0;
|
||||||
const { line, column } = lineAndColumn(text, index);
|
const { line, column } = lineAndColumn(text, index);
|
||||||
matches.push({
|
matches.push({
|
||||||
@@ -136,7 +158,11 @@ for (const filePath of listFiles(repoRoot)) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (writeMode && !writeModeSkip.has(path.normalize(relativePath))) {
|
if (
|
||||||
|
writeMode &&
|
||||||
|
!writeModeSkip.has(path.normalize(relativePath)) &&
|
||||||
|
canAutoWrite(relativePath)
|
||||||
|
) {
|
||||||
const sanitized = sanitizeText(text);
|
const sanitized = sanitizeText(text);
|
||||||
if (sanitized !== text) {
|
if (sanitized !== text) {
|
||||||
fs.writeFileSync(filePath, sanitized, 'utf8');
|
fs.writeFileSync(filePath, sanitized, 'utf8');
|
||||||
|
|||||||
@@ -306,10 +306,10 @@ function evaluate(rawInput) {
|
|||||||
for (const file of filesToCheck) {
|
for (const file of filesToCheck) {
|
||||||
const fileIssues = findFileIssues(file);
|
const fileIssues = findFileIssues(file);
|
||||||
if (fileIssues.length > 0) {
|
if (fileIssues.length > 0) {
|
||||||
console.error(`\n ${file}`);
|
console.error(`\n[FILE] ${file}`);
|
||||||
for (const issue of fileIssues) {
|
for (const issue of fileIssues) {
|
||||||
const icon = issue.severity === 'error' ? 'FAIL:' : issue.severity === 'warning' ? 'WARNING:' : '';
|
const label = issue.severity === 'error' ? 'ERROR' : issue.severity === 'warning' ? 'WARNING' : 'INFO';
|
||||||
console.error(` ${icon} Line ${issue.line}: ${issue.message}`);
|
console.error(` ${label} Line ${issue.line}: ${issue.message}`);
|
||||||
totalIssues++;
|
totalIssues++;
|
||||||
if (issue.severity === 'error') errorCount++;
|
if (issue.severity === 'error') errorCount++;
|
||||||
if (issue.severity === 'warning') warningCount++;
|
if (issue.severity === 'warning') warningCount++;
|
||||||
@@ -323,9 +323,9 @@ function evaluate(rawInput) {
|
|||||||
if (messageValidation && messageValidation.issues.length > 0) {
|
if (messageValidation && messageValidation.issues.length > 0) {
|
||||||
console.error('\nCommit Message Issues:');
|
console.error('\nCommit Message Issues:');
|
||||||
for (const issue of messageValidation.issues) {
|
for (const issue of messageValidation.issues) {
|
||||||
console.error(` WARNING: ${issue.message}`);
|
console.error(` WARNING ${issue.message}`);
|
||||||
if (issue.suggestion) {
|
if (issue.suggestion) {
|
||||||
console.error(` ${issue.suggestion}`);
|
console.error(` TIP ${issue.suggestion}`);
|
||||||
}
|
}
|
||||||
totalIssues++;
|
totalIssues++;
|
||||||
warningCount++;
|
warningCount++;
|
||||||
@@ -361,7 +361,7 @@ function evaluate(rawInput) {
|
|||||||
console.error(`\nSummary: ${totalIssues} issue(s) found (${errorCount} error(s), ${warningCount} warning(s), ${infoCount} info)`);
|
console.error(`\nSummary: ${totalIssues} issue(s) found (${errorCount} error(s), ${warningCount} warning(s), ${infoCount} info)`);
|
||||||
|
|
||||||
if (errorCount > 0) {
|
if (errorCount > 0) {
|
||||||
console.error('\n[Hook] FAIL: Commit blocked due to critical issues. Fix them before committing.');
|
console.error('\n[Hook] ERROR: Commit blocked due to critical issues. Fix them before committing.');
|
||||||
return { output: rawInput, exitCode: 2 };
|
return { output: rawInput, exitCode: 2 };
|
||||||
} else {
|
} else {
|
||||||
console.error('\n[Hook] WARNING: Warnings found. Consider fixing them, but commit is allowed.');
|
console.error('\n[Hook] WARNING: Warnings found. Consider fixing them, but commit is allowed.');
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ function sleep(ms) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function animateProgress(label, steps, callback) {
|
async function animateProgress(label, steps, callback) {
|
||||||
process.stdout.write(`\n${chalk.cyan('')} ${label}...\n`);
|
process.stdout.write(`\n${chalk.cyan('[RUN]')} ${label}...\n`);
|
||||||
|
|
||||||
for (let i = 0; i < steps.length; i++) {
|
for (let i = 0; i < steps.length; i++) {
|
||||||
const step = steps[i];
|
const step = steps[i];
|
||||||
@@ -72,7 +72,7 @@ async function animateProgress(label, steps, callback) {
|
|||||||
await sleep(step.duration || 500);
|
await sleep(step.duration || 500);
|
||||||
process.stdout.clearLine?.(0) || process.stdout.write('\r');
|
process.stdout.clearLine?.(0) || process.stdout.write('\r');
|
||||||
process.stdout.cursorTo?.(0) || process.stdout.write('\r');
|
process.stdout.cursorTo?.(0) || process.stdout.write('\r');
|
||||||
process.stdout.write(` ${chalk.green('✓')} ${step.name}\n`);
|
process.stdout.write(` ${chalk.green('[DONE]')} ${step.name}\n`);
|
||||||
if (callback) callback(step, i);
|
if (callback) callback(step, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -147,10 +147,10 @@ ${chalk.bold('Files Tracked:')} ${chalk.green(data.files)}
|
|||||||
console.log(chalk.bold(chalk.green('Generation Complete!')));
|
console.log(chalk.bold(chalk.green('Generation Complete!')));
|
||||||
console.log(chalk.gray('─'.repeat(50)));
|
console.log(chalk.gray('─'.repeat(50)));
|
||||||
console.log(`
|
console.log(`
|
||||||
${chalk.green('')} ${chalk.bold('Skill File:')}
|
${chalk.green('-')} ${chalk.bold('Skill File:')}
|
||||||
${chalk.cyan(skillPath)}
|
${chalk.cyan(skillPath)}
|
||||||
|
|
||||||
${chalk.green('')} ${chalk.bold('Instincts File:')}
|
${chalk.green('-')} ${chalk.bold('Instincts File:')}
|
||||||
${chalk.cyan(instinctsPath)}
|
${chalk.cyan(instinctsPath)}
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ if (projectPath && projectPath !== cwd) {
|
|||||||
if (existsSync(projectPath)) {
|
if (existsSync(projectPath)) {
|
||||||
console.log(`→ cd ${projectPath}`);
|
console.log(`→ cd ${projectPath}`);
|
||||||
} else {
|
} else {
|
||||||
console.log(`WARNING: Path not found: ${projectPath}`);
|
console.log(`WARNING Path not found: ${projectPath}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ function main() {
|
|||||||
// Check if previous session ID exists in sessions array
|
// Check if previous session ID exists in sessions array
|
||||||
const alreadySaved = context.sessions?.some(s => s.id === prevSession.sessionId);
|
const alreadySaved = context.sessions?.some(s => s.id === prevSession.sessionId);
|
||||||
if (!alreadySaved) {
|
if (!alreadySaved) {
|
||||||
summaryLines.push(`WARNING: Last session wasn't saved — run /ck:save to capture it`);
|
summaryLines.push(`WARNING Last session wasn't saved — run /ck:save to capture it`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +142,7 @@ function main() {
|
|||||||
const claudeMdGoal = extractClaudeMdGoal(cwd);
|
const claudeMdGoal = extractClaudeMdGoal(cwd);
|
||||||
if (claudeMdGoal && context.goal &&
|
if (claudeMdGoal && context.goal &&
|
||||||
claudeMdGoal.toLowerCase().trim() !== context.goal.toLowerCase().trim()) {
|
claudeMdGoal.toLowerCase().trim() !== context.goal.toLowerCase().trim()) {
|
||||||
summaryLines.push(`WARNING: Goal mismatch — ck: "${context.goal.slice(0, 40)}" · CLAUDE.md: "${claudeMdGoal.slice(0, 40)}"`);
|
summaryLines.push(`WARNING Goal mismatch — ck: "${context.goal.slice(0, 40)}" · CLAUDE.md: "${claudeMdGoal.slice(0, 40)}"`);
|
||||||
summaryLines.push(` Run /ck:save with updated goal to sync`);
|
summaryLines.push(` Run /ck:save with updated goal to sync`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,7 +165,7 @@ function main() {
|
|||||||
'```',
|
'```',
|
||||||
``,
|
``,
|
||||||
`After the block, add one line: "Ready — what are we working on?"`,
|
`After the block, add one line: "Ready — what are we working on?"`,
|
||||||
`If you see WARNING: warnings above, mention them briefly after the block.`,
|
`If you see WARNING lines above, mention them briefly after the block.`,
|
||||||
].join('\n'));
|
].join('\n'));
|
||||||
|
|
||||||
return parts;
|
return parts;
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ function resetAliases() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function runTests() {
|
function runTests() {
|
||||||
|
const rocketEmoji = String.fromCodePoint(0x1F680);
|
||||||
console.log('\n=== Testing session-aliases.js ===\n');
|
console.log('\n=== Testing session-aliases.js ===\n');
|
||||||
|
|
||||||
let passed = 0;
|
let passed = 0;
|
||||||
@@ -1441,7 +1442,7 @@ function runTests() {
|
|||||||
'CJK characters should be rejected');
|
'CJK characters should be rejected');
|
||||||
|
|
||||||
// Emoji
|
// Emoji
|
||||||
const emojiResult = aliases.resolveAlias('rocket-');
|
const emojiResult = aliases.resolveAlias(`rocket-${rocketEmoji}`);
|
||||||
assert.strictEqual(emojiResult, null,
|
assert.strictEqual(emojiResult, null,
|
||||||
'Emoji should be rejected by the ASCII-only regex');
|
'Emoji should be rejected by the ASCII-only regex');
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ function test(name, fn) {
|
|||||||
|
|
||||||
// Test suite
|
// Test suite
|
||||||
function runTests() {
|
function runTests() {
|
||||||
|
const rocketParty = String.fromCodePoint(0x1F680, 0x1F389);
|
||||||
|
const partyEmoji = String.fromCodePoint(0x1F389);
|
||||||
console.log('\n=== Testing utils.js ===\n');
|
console.log('\n=== Testing utils.js ===\n');
|
||||||
|
|
||||||
let passed = 0;
|
let passed = 0;
|
||||||
@@ -166,9 +168,12 @@ function runTests() {
|
|||||||
if (test('sanitizeSessionId returns stable hashes for non-ASCII values', () => {
|
if (test('sanitizeSessionId returns stable hashes for non-ASCII values', () => {
|
||||||
const chinese = utils.sanitizeSessionId('我的项目');
|
const chinese = utils.sanitizeSessionId('我的项目');
|
||||||
const cyrillic = utils.sanitizeSessionId('проект');
|
const cyrillic = utils.sanitizeSessionId('проект');
|
||||||
|
const emoji = utils.sanitizeSessionId(rocketParty);
|
||||||
assert.ok(/^[a-f0-9]{8}$/.test(chinese), `Expected 8-char hash, got: ${chinese}`);
|
assert.ok(/^[a-f0-9]{8}$/.test(chinese), `Expected 8-char hash, got: ${chinese}`);
|
||||||
assert.ok(/^[a-f0-9]{8}$/.test(cyrillic), `Expected 8-char hash, got: ${cyrillic}`);
|
assert.ok(/^[a-f0-9]{8}$/.test(cyrillic), `Expected 8-char hash, got: ${cyrillic}`);
|
||||||
|
assert.ok(/^[a-f0-9]{8}$/.test(emoji), `Expected 8-char hash, got: ${emoji}`);
|
||||||
assert.notStrictEqual(chinese, cyrillic);
|
assert.notStrictEqual(chinese, cyrillic);
|
||||||
|
assert.notStrictEqual(chinese, emoji);
|
||||||
assert.strictEqual(utils.sanitizeSessionId('日本語プロジェクト'), utils.sanitizeSessionId('日本語プロジェクト'));
|
assert.strictEqual(utils.sanitizeSessionId('日本語プロジェクト'), utils.sanitizeSessionId('日本語プロジェクト'));
|
||||||
})) passed++; else failed++;
|
})) passed++; else failed++;
|
||||||
|
|
||||||
@@ -704,7 +709,7 @@ function runTests() {
|
|||||||
if (test('writeFile handles unicode content', () => {
|
if (test('writeFile handles unicode content', () => {
|
||||||
const testFile = path.join(utils.getTempDir(), `utils-test-${Date.now()}.txt`);
|
const testFile = path.join(utils.getTempDir(), `utils-test-${Date.now()}.txt`);
|
||||||
try {
|
try {
|
||||||
const unicode = '日本語テスト 中文 émojis';
|
const unicode = `日本語テスト ${String.fromCodePoint(0x1F680)} émojis`;
|
||||||
utils.writeFile(testFile, unicode);
|
utils.writeFile(testFile, unicode);
|
||||||
const content = utils.readFile(testFile);
|
const content = utils.readFile(testFile);
|
||||||
assert.strictEqual(content, unicode);
|
assert.strictEqual(content, unicode);
|
||||||
@@ -1868,18 +1873,18 @@ function runTests() {
|
|||||||
}
|
}
|
||||||
})) passed++; else failed++;
|
})) passed++; else failed++;
|
||||||
|
|
||||||
// ── Round 108: grepFile with Unicode content — UTF-16 string matching on split lines ──
|
// ── Round 108: grepFile with Unicode/emoji content — UTF-16 string matching on split lines ──
|
||||||
console.log('\nRound 108: grepFile (Unicode — regex matching on UTF-16 split lines):');
|
console.log('\nRound 108: grepFile (Unicode/emoji — regex matching on UTF-16 split lines):');
|
||||||
if (test('grepFile finds Unicode patterns across lines', () => {
|
if (test('grepFile finds Unicode emoji patterns across lines', () => {
|
||||||
const tmpDir = fs.mkdtempSync(path.join(utils.getTempDir(), 'r108-grep-unicode-'));
|
const tmpDir = fs.mkdtempSync(path.join(utils.getTempDir(), 'r108-grep-unicode-'));
|
||||||
const testFile = path.join(tmpDir, 'test.txt');
|
const testFile = path.join(tmpDir, 'test.txt');
|
||||||
try {
|
try {
|
||||||
fs.writeFileSync(testFile, '猫 celebration\nnormal line\n猫 party\n日本語テスト');
|
fs.writeFileSync(testFile, `${partyEmoji} celebration\nnormal line\n${partyEmoji} party\n日本語テスト`);
|
||||||
const unicodeResults = utils.grepFile(testFile, /猫/);
|
const emojiResults = utils.grepFile(testFile, new RegExp(partyEmoji, 'u'));
|
||||||
assert.strictEqual(unicodeResults.length, 2,
|
assert.strictEqual(emojiResults.length, 2,
|
||||||
'Should find Unicode matches on 2 lines (lines 1 and 3)');
|
'Should find emoji on 2 lines (lines 1 and 3)');
|
||||||
assert.strictEqual(unicodeResults[0].lineNumber, 1);
|
assert.strictEqual(emojiResults[0].lineNumber, 1);
|
||||||
assert.strictEqual(unicodeResults[1].lineNumber, 3);
|
assert.strictEqual(emojiResults[1].lineNumber, 3);
|
||||||
const cjkResults = utils.grepFile(testFile, /日本語/);
|
const cjkResults = utils.grepFile(testFile, /日本語/);
|
||||||
assert.strictEqual(cjkResults.length, 1,
|
assert.strictEqual(cjkResults.length, 1,
|
||||||
'Should find CJK characters on line 4');
|
'Should find CJK characters on line 4');
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ for (const testFile of testFiles) {
|
|||||||
const displayPath = testFile.split(path.sep).join('/');
|
const displayPath = testFile.split(path.sep).join('/');
|
||||||
|
|
||||||
if (!fs.existsSync(testPath)) {
|
if (!fs.existsSync(testPath)) {
|
||||||
console.log(`WARNING: Skipping ${displayPath} (file not found)`);
|
console.log(`WARNING Skipping ${displayPath} (file not found)`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ function makeTempRoot(prefix) {
|
|||||||
const warningEmoji = String.fromCodePoint(0x26A0, 0xFE0F);
|
const warningEmoji = String.fromCodePoint(0x26A0, 0xFE0F);
|
||||||
const toolsEmoji = String.fromCodePoint(0x1F6E0, 0xFE0F);
|
const toolsEmoji = String.fromCodePoint(0x1F6E0, 0xFE0F);
|
||||||
const zeroWidthSpace = String.fromCodePoint(0x200B);
|
const zeroWidthSpace = String.fromCodePoint(0x200B);
|
||||||
|
const rocketEmoji = String.fromCodePoint(0x1F680);
|
||||||
|
|
||||||
let passed = 0;
|
let passed = 0;
|
||||||
let failed = 0;
|
let failed = 0;
|
||||||
@@ -78,6 +79,36 @@ if (
|
|||||||
passed++;
|
passed++;
|
||||||
else failed++;
|
else failed++;
|
||||||
|
|
||||||
|
if (
|
||||||
|
test('write mode does not rewrite executable files', () => {
|
||||||
|
const root = makeTempRoot('ecc-unicode-code-');
|
||||||
|
fs.mkdirSync(path.join(root, 'scripts'), { recursive: true });
|
||||||
|
const scriptFile = path.join(root, 'scripts', 'sample.js');
|
||||||
|
const original = `const label = "Launch ${rocketEmoji}";\n`;
|
||||||
|
fs.writeFileSync(scriptFile, original);
|
||||||
|
|
||||||
|
const result = runCheck(root, ['--write']);
|
||||||
|
assert.notStrictEqual(result.status, 0, result.stdout + result.stderr);
|
||||||
|
assert.match(result.stderr, /scripts\/sample\.js:1:23 emoji U\+1F680/);
|
||||||
|
assert.strictEqual(fs.readFileSync(scriptFile, 'utf8'), original);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
passed++;
|
||||||
|
else failed++;
|
||||||
|
|
||||||
|
if (
|
||||||
|
test('plain symbols like copyright remain allowed', () => {
|
||||||
|
const root = makeTempRoot('ecc-unicode-symbols-');
|
||||||
|
fs.mkdirSync(path.join(root, 'docs'), { recursive: true });
|
||||||
|
fs.writeFileSync(path.join(root, 'docs', 'legal.md'), 'Copyright © ECC\nTrademark ® ECC\n');
|
||||||
|
|
||||||
|
const result = runCheck(root);
|
||||||
|
assert.strictEqual(result.status, 0, result.stdout + result.stderr);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
passed++;
|
||||||
|
else failed++;
|
||||||
|
|
||||||
console.log(`\nPassed: ${passed}`);
|
console.log(`\nPassed: ${passed}`);
|
||||||
console.log(`Failed: ${failed}`);
|
console.log(`Failed: ${failed}`);
|
||||||
process.exit(failed > 0 ? 1 : 0);
|
process.exit(failed > 0 ? 1 : 0);
|
||||||
|
|||||||
Reference in New Issue
Block a user