Skip to content

Commit

Permalink
Provide a browser-only export (#17)
Browse files Browse the repository at this point in the history
Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
  • Loading branch information
marcelgerber and sindresorhus committed Sep 24, 2020
1 parent 4e8e737 commit d8795d3
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 69 deletions.
8 changes: 8 additions & 0 deletions filenamify-path.d.ts
@@ -0,0 +1,8 @@
import filenamify = require('./filenamify');

/**
Convert the filename in a path a valid filename and return the augmented path.
*/
declare const filenamifyPath: (path: string, options?: filenamify.Options) => string;

export = filenamifyPath;
10 changes: 10 additions & 0 deletions filenamify-path.js
@@ -0,0 +1,10 @@
'use strict';
const path = require('path');
const filenamify = require('./filenamify');

const filenamifyPath = (filePath, options) => {
filePath = path.resolve(filePath);
return path.join(path.dirname(filePath), filenamify(path.basename(filePath), options));
};

module.exports = filenamifyPath;
39 changes: 39 additions & 0 deletions filenamify.d.ts
@@ -0,0 +1,39 @@
declare namespace filenamify {
interface Options {
/**
String to use as replacement for reserved filename characters.
Cannot contain: `<` `>` `:` `"` `/` `\` `|` `?` `*`
@default '!'
*/
readonly replacement?: string;

/**
Truncate the filename to the given length.
Systems generally allow up to 255 characters, but we default to 100 for usability reasons.
@default 100
*/
readonly maxLength?: number;
}
}

/**
Convert a string to a valid filename.
@example
```
import filenamify = require('filenamify');
filenamify('<foo/bar>');
//=> 'foo!bar'
filenamify('foo:"bar"', {replacement: '🐴'});
//=> 'foo🐴bar'
```
*/
declare const filenamify: (string: string, options?: filenamify.Options) => string;

export = filenamify;
38 changes: 38 additions & 0 deletions filenamify.js
@@ -0,0 +1,38 @@
'use strict';
const trimRepeated = require('trim-repeated');
const filenameReservedRegex = require('filename-reserved-regex');
const stripOuter = require('strip-outer');

// Doesn't make sense to have longer filenames
const MAX_FILENAME_LENGTH = 100;

const reControlChars = /[\u0000-\u001f\u0080-\u009f]/g; // eslint-disable-line no-control-regex
const reRelativePath = /^\.+/;

const filenamify = (string, options = {}) => {
if (typeof string !== 'string') {
throw new TypeError('Expected a string');
}

const replacement = options.replacement === undefined ? '!' : options.replacement;

if (filenameReservedRegex().test(replacement) && reControlChars.test(replacement)) {
throw new Error('Replacement string cannot contain reserved filename characters');
}

string = string.replace(filenameReservedRegex(), replacement);
string = string.replace(reControlChars, replacement);
string = string.replace(reRelativePath, replacement);

if (replacement.length > 0) {
string = trimRepeated(string, replacement);
string = string.length > 1 ? stripOuter(string, replacement) : string;
}

string = filenameReservedRegex.windowsNames().test(string) ? string + replacement : string;
string = string.slice(0, typeof options.maxLength === 'number' ? options.maxLength : MAX_FILENAME_LENGTH);

return string;
};

module.exports = filenamify;
32 changes: 5 additions & 27 deletions index.d.ts
@@ -1,26 +1,7 @@
declare namespace filenamify {
interface Options {
/**
String to use as replacement for reserved filename characters.
import filenamify = require('./filenamify');
import filenamifyPath = require('./filenamify-path');

Cannot contain: `<` `>` `:` `"` `/` `\` `|` `?` `*`
@default '!'
*/
readonly replacement?: string;

/**
Truncate the filename to the given length.
Systems generally allow up to 255 characters, but we default to 100 for usability reasons.
@default 100
*/
readonly maxLength?: number;
}
}

declare const filenamify: {
declare const filenamifyCombined: {
/**
Convert a string to a valid filename.
Expand All @@ -37,10 +18,7 @@ declare const filenamify: {
*/
(string: string, options?: filenamify.Options): string;

/**
Convert the filename in a path a valid filename and return the augmented path.
*/
path(path: string, options?: filenamify.Options): string;
path: typeof filenamifyPath;
};

export = filenamify;
export = filenamifyCombined;
44 changes: 4 additions & 40 deletions index.js
@@ -1,44 +1,8 @@
'use strict';
const path = require('path');
const trimRepeated = require('trim-repeated');
const filenameReservedRegex = require('filename-reserved-regex');
const stripOuter = require('strip-outer');
const filenamify = require('./filenamify');
const filenamifyPath = require('./filenamify-path');

// Doesn't make sense to have longer filenames
const MAX_FILENAME_LENGTH = 100;

const reControlChars = /[\u0000-\u001f\u0080-\u009f]/g; // eslint-disable-line no-control-regex
const reRelativePath = /^\.+/;

const filenamify = (string, options = {}) => {
if (typeof string !== 'string') {
throw new TypeError('Expected a string');
}

const replacement = options.replacement === undefined ? '!' : options.replacement;

if (filenameReservedRegex().test(replacement) && reControlChars.test(replacement)) {
throw new Error('Replacement string cannot contain reserved filename characters');
}

string = string.replace(filenameReservedRegex(), replacement);
string = string.replace(reControlChars, replacement);
string = string.replace(reRelativePath, replacement);

if (replacement.length > 0) {
string = trimRepeated(string, replacement);
string = string.length > 1 ? stripOuter(string, replacement) : string;
}

string = filenameReservedRegex.windowsNames().test(string) ? string + replacement : string;
string = string.slice(0, typeof options.maxLength === 'number' ? options.maxLength : MAX_FILENAME_LENGTH);

return string;
};

filenamify.path = (filePath, options) => {
filePath = path.resolve(filePath);
return path.join(path.dirname(filePath), filenamify(path.basename(filePath), options));
};
const filenamifyCombined = filenamify;
filenamifyCombined.path = filenamifyPath;

module.exports = filenamify;
12 changes: 10 additions & 2 deletions package.json
Expand Up @@ -16,9 +16,17 @@
"test": "xo && ava && tsd"
},
"files": [
"index.js",
"index.d.ts"
"filenamify-path.d.ts",
"filenamify-path.js",
"filenamify.d.ts",
"filenamify.js",
"index.d.ts",
"index.js"
],
"exports": {
".": "./index.js",
"./browser": "./filenamify.js"
},
"keywords": [
"filename",
"safe",
Expand Down
11 changes: 11 additions & 0 deletions readme.md
Expand Up @@ -57,6 +57,17 @@ Truncate the filename to the given length.

Systems generally allow up to 255 characters, but we default to 100 for usability reasons.

## Browser-only import

You can also import `filenamify/browser`, which only imports `filenamify` and not `filenamify.path`, which relies on `path` being available or polyfilled. Importing `filenamify` this way is therefore useful when it is shipped using `webpack` or similar tools, and if `filenamify.path` is not needed.

```js
const filenamify = require('filenamify/browser');

filenamify('<foo/bar>');
//=> 'foo!bar'
```


## Related

Expand Down

0 comments on commit d8795d3

Please sign in to comment.