Skip to content

Commit ff8d131

Browse files
authored
Merge pull request #351 from C2FO/issue340
fix(parse): Handle escaped escape properly #340
2 parents 314f247 + 78d9b16 commit ff8d131

File tree

6 files changed

+72
-2
lines changed

6 files changed

+72
-2
lines changed

.run/Run Examples.run.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<component name="ProjectRunConfigurationManager">
2+
<configuration default="false" name="Run Examples" type="js.build_tools.npm">
3+
<package-json value="$PROJECT_DIR$/package.json" />
4+
<command value="run" />
5+
<scripts>
6+
<script value="examples" />
7+
</scripts>
8+
<node-interpreter value="project" />
9+
<envs />
10+
<method v="2" />
11+
</configuration>
12+
</component>

.run/Serve Docs.run.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<component name="ProjectRunConfigurationManager">
2+
<configuration default="false" name="Serve Docs" type="js.build_tools.npm">
3+
<package-json value="$PROJECT_DIR$/documentation/package.json" />
4+
<command value="run" />
5+
<scripts>
6+
<script value="start" />
7+
</scripts>
8+
<node-interpreter value="project" />
9+
<envs />
10+
<method v="2" />
11+
</configuration>
12+
</component>

.run/jest.config.js.run.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<component name="ProjectRunConfigurationManager">
2+
<configuration default="false" name="Run Tests" type="JavaScriptTestRunnerJest">
3+
<config-file value="$PROJECT_DIR$/jest.config.js" />
4+
<node-interpreter value="project" />
5+
<node-options value="" />
6+
<jest-package value="$PROJECT_DIR$/node_modules/jest" />
7+
<working-dir value="$PROJECT_DIR$/packages/parse" />
8+
<jest-options value="--runInBand --coverage" />
9+
<envs />
10+
<scope-kind value="ALL" />
11+
<method v="2" />
12+
</configuration>
13+
</component>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { EOL } from 'os';
2+
import { parseString, RowMap, RowArray } from '../../src';
3+
4+
describe('Issue #340 - https://github.com/C2FO/fast-csv/issues/340', () => {
5+
const CSV_CONTENT = ['Col1', `"A '"Good'" row''"`, 'Row 2'].join(EOL);
6+
const expectedRows = [{ Col1: `A "Good" row'` }, { Col1: 'Row 2' }];
7+
8+
it('handle a trailing escape', (done) => {
9+
const invalid: RowArray[] = [];
10+
const rows: RowMap[] = [];
11+
parseString(CSV_CONTENT, { headers: true, escape: "'" })
12+
.on('data-invalid', (row: RowArray) => invalid.push(row))
13+
.on('data', (r: RowMap) => rows.push(r))
14+
.on('error', done)
15+
.on('end', (count: number) => {
16+
expect(rows).toEqual(expectedRows);
17+
expect(invalid).toHaveLength(0);
18+
expect(count).toBe(expectedRows.length + invalid.length);
19+
done();
20+
});
21+
});
22+
});

packages/parse/__tests__/parser/column/QuotedColumnParser.spec.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,13 @@ describe('QuotedColumnParser', () => {
481481
expect(scanner.lineFromCursor).toBe(',"world"');
482482
});
483483

484+
it('should parse an escape followed by another escape', () => {
485+
const line = '"hello$$","world"';
486+
const { scanner, col } = parse(line, true, { escape: '$' });
487+
expect(col).toBe('hello$');
488+
expect(scanner.lineFromCursor).toBe(',"world"');
489+
});
490+
484491
it('should parse a quoted col up to a LF', () => {
485492
const line = '"hello"\n"world"';
486493
const { scanner, col } = parse(line, true, { escape: '$' });

packages/parse/src/parser/column/QuotedColumnParser.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export class QuotedColumnParser {
3535
throw new Error(
3636
`Parse Error: missing closing: '${
3737
this.parserOptions.quote
38-
}' in line: at '${scanner.lineFromCursor.replace(/[r\n]/g, "\\n'")}'`,
38+
}' in line: at '${scanner.lineFromCursor.replace(/[\r\n]/g, "\\n'")}'`,
3939
);
4040
}
4141
return null;
@@ -62,7 +62,11 @@ export class QuotedColumnParser {
6262
const tokenFollowingEscape = scanner.nextCharacterToken;
6363
// if the character following the escape is a quote character then just add
6464
// the quote and advance to that character
65-
if (tokenFollowingEscape !== null && isTokenQuote(tokenFollowingEscape, parserOptions)) {
65+
if (
66+
tokenFollowingEscape !== null &&
67+
(isTokenQuote(tokenFollowingEscape, parserOptions) ||
68+
isTokenEscapeCharacter(tokenFollowingEscape, parserOptions))
69+
) {
6670
characters.push(tokenFollowingEscape.token);
6771
nextToken = tokenFollowingEscape;
6872
} else if (isQuote) {

0 commit comments

Comments
 (0)