From 7fb570e503f4498028c759cacc159450516b304a Mon Sep 17 00:00:00 2001 From: Ramy Ben Aroya Date: Mon, 23 Apr 2018 14:16:29 +0300 Subject: [PATCH 1/2] fix: use safe-decode-uri-component This takes care of file names with `'%'` character. File names like these can be automatically created by IDEs for backup. Also, added a test for adding file with `'%'` character to `InMemoryFileSystem`. Fixes #422 --- package.json | 1 + src/test/memfs.test.ts | 8 ++++++++ src/typings/safe-decode-uri-component.d.ts | 3 +++ src/util.ts | 5 +++-- tsconfig.json | 1 + 5 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 src/typings/safe-decode-uri-component.d.ts diff --git a/package.json b/package.json index 1ff910e1a..f7c784d30 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "object-hash": "^1.1.8", "opentracing": "^0.14.0", "rxjs": "^5.5.0", + "safe-decode-uri-component": "^1.1.3", "semaphore-async-await": "^1.5.1", "string-similarity": "^1.1.0", "typescript": "2.7.2", diff --git a/src/test/memfs.test.ts b/src/test/memfs.test.ts index c5ed629bb..aac2bead4 100644 --- a/src/test/memfs.test.ts +++ b/src/test/memfs.test.ts @@ -18,6 +18,14 @@ describe('memfs.ts', () => { sinon.assert.calledOnce(listener) sinon.assert.calledWithExactly(listener, 'file:///foo/bar.txt', undefined) }) + it('should add a URI with `%` character and emit an event', () => { + const listener = sinon.spy() + const fs = new InMemoryFileSystem('/') + fs.on('add', listener) + fs.add('file:///foo/%bar.txt') + sinon.assert.calledOnce(listener) + sinon.assert.calledWithExactly(listener, 'file:///foo/%bar.txt', undefined) + }) it('should add content for a URI and emit an event', () => { const listener = sinon.spy() const fs = new InMemoryFileSystem('/') diff --git a/src/typings/safe-decode-uri-component.d.ts b/src/typings/safe-decode-uri-component.d.ts new file mode 100644 index 000000000..196ea6bf6 --- /dev/null +++ b/src/typings/safe-decode-uri-component.d.ts @@ -0,0 +1,3 @@ +declare module 'safe-decode-uri-component' { + export default function safeDecodeURIComponent(uriComponent: string): string +} diff --git a/src/util.ts b/src/util.ts index a90859d91..83b54be7a 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,5 +1,6 @@ import { escapePathComponent } from 'fast-json-patch' import { Observable } from 'rxjs' +import safeDecodeURIComponent from 'safe-decode-uri-component' import { compareTwoStrings } from 'string-similarity' import * as ts from 'typescript' import * as url from 'url' @@ -43,7 +44,7 @@ export function normalizeUri(uri: string): string { if (!parts.pathname) { return uri } - const pathParts = parts.pathname.split('/').map(segment => encodeURIComponent(decodeURIComponent(segment))) + const pathParts = parts.pathname.split('/').map(segment => encodeURIComponent(safeDecodeURIComponent(segment))) // Decode Windows drive letter colon if (/^[a-z]%3A$/i.test(pathParts[1])) { pathParts[1] = decodeURIComponent(pathParts[1]) @@ -95,7 +96,7 @@ export function uri2path(uri: string): string { filePath = filePath.substr(1).replace(/\//g, '\\') } - return decodeURIComponent(filePath) + return safeDecodeURIComponent(filePath) } const jstsPattern = /\.[tj]sx?$/ diff --git a/tsconfig.json b/tsconfig.json index d28c1e213..b3bc93d7b 100755 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,6 +9,7 @@ "sourceMap": true, "declaration": true, "strictFunctionTypes": false, + "esModuleInterop": true, "strictPropertyInitialization": false, "plugins": [ { From 9ed359ebc8e926a7635774d1c0d1c8fd4e3106bf Mon Sep 17 00:00:00 2001 From: Ramy Ben Aroya Date: Mon, 23 Apr 2018 14:38:51 +0300 Subject: [PATCH 2/2] fix: dont import rimraf with property notation Since esModuleInterop set to true, `rimraf` can be imported like a regular module --- src/test/fs.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/fs.test.ts b/src/test/fs.test.ts index 8ca580e27..fc0046f1d 100644 --- a/src/test/fs.test.ts +++ b/src/test/fs.test.ts @@ -2,7 +2,7 @@ import * as chai from 'chai' import chaiAsPromised = require('chai-as-promised') import * as fs from 'mz/fs' import * as path from 'path' -import * as rimraf from 'rimraf' +import rimraf from 'rimraf' import * as temp from 'temp' import { LocalFileSystem } from '../fs' import { path2uri } from '../util'