Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .run/Run Examples.run.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Run Examples" type="js.build_tools.npm">
<package-json value="$PROJECT_DIR$/package.json" />
<command value="run" />
<scripts>
<script value="examples" />
</scripts>
<node-interpreter value="project" />
<envs />
<method v="2" />
</configuration>
</component>
12 changes: 12 additions & 0 deletions .run/Serve Docs.run.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Serve Docs" type="js.build_tools.npm">
<package-json value="$PROJECT_DIR$/documentation/package.json" />
<command value="run" />
<scripts>
<script value="start" />
</scripts>
<node-interpreter value="project" />
<envs />
<method v="2" />
</configuration>
</component>
13 changes: 13 additions & 0 deletions .run/jest.config.js.run.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Run Tests" type="JavaScriptTestRunnerJest">
<config-file value="$PROJECT_DIR$/jest.config.js" />
<node-interpreter value="project" />
<node-options value="" />
<jest-package value="$PROJECT_DIR$/node_modules/jest" />
<working-dir value="$PROJECT_DIR$/packages/parse" />
<jest-options value="--runInBand --coverage" />
<envs />
<scope-kind value="ALL" />
<method v="2" />
</configuration>
</component>
22 changes: 22 additions & 0 deletions packages/parse/__tests__/issues/issue340.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { EOL } from 'os';
import { parseString, RowMap, RowArray } from '../../src';

describe('Issue #340 - https://github.com/C2FO/fast-csv/issues/340', () => {
const CSV_CONTENT = ['Col1', `"A '"Good'" row''"`, 'Row 2'].join(EOL);
const expectedRows = [{ Col1: `A "Good" row'` }, { Col1: 'Row 2' }];

it('handle a trailing escape', (done) => {
const invalid: RowArray[] = [];
const rows: RowMap[] = [];
parseString(CSV_CONTENT, { headers: true, escape: "'" })
.on('data-invalid', (row: RowArray) => invalid.push(row))
.on('data', (r: RowMap) => rows.push(r))
.on('error', done)
.on('end', (count: number) => {
expect(rows).toEqual(expectedRows);
expect(invalid).toHaveLength(0);
expect(count).toBe(expectedRows.length + invalid.length);
done();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,13 @@ describe('QuotedColumnParser', () => {
expect(scanner.lineFromCursor).toBe(',"world"');
});

it('should parse an escape followed by another escape', () => {
const line = '"hello$$","world"';
const { scanner, col } = parse(line, true, { escape: '$' });
expect(col).toBe('hello$');
expect(scanner.lineFromCursor).toBe(',"world"');
});

it('should parse a quoted col up to a LF', () => {
const line = '"hello"\n"world"';
const { scanner, col } = parse(line, true, { escape: '$' });
Expand Down
8 changes: 6 additions & 2 deletions packages/parse/src/parser/column/QuotedColumnParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export class QuotedColumnParser {
throw new Error(
`Parse Error: missing closing: '${
this.parserOptions.quote
}' in line: at '${scanner.lineFromCursor.replace(/[r\n]/g, "\\n'")}'`,
}' in line: at '${scanner.lineFromCursor.replace(/[\r\n]/g, "\\n'")}'`,
);
}
return null;
Expand All @@ -62,7 +62,11 @@ export class QuotedColumnParser {
const tokenFollowingEscape = scanner.nextCharacterToken;
// if the character following the escape is a quote character then just add
// the quote and advance to that character
if (tokenFollowingEscape !== null && isTokenQuote(tokenFollowingEscape, parserOptions)) {
if (
tokenFollowingEscape !== null &&
(isTokenQuote(tokenFollowingEscape, parserOptions) ||
isTokenEscapeCharacter(tokenFollowingEscape, parserOptions))
) {
characters.push(tokenFollowingEscape.token);
nextToken = tokenFollowingEscape;
} else if (isQuote) {
Expand Down