Skip to content

Commit

Permalink
feat(@formatjs/intl-utils): Add interpolateName
Browse files Browse the repository at this point in the history
fix(@formatjs/ts-transformer): Fix interpolation issue not matching native
fix(@formatjs/cli): Fix interpolation issue not matching native
  • Loading branch information
Long Ho committed Jun 4, 2020
1 parent d25a809 commit 4d09912
Show file tree
Hide file tree
Showing 9 changed files with 172 additions and 6 deletions.
4 changes: 2 additions & 2 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@
"commander": "5.0.0",
"fs-extra": "^9.0.0",
"glob": "^7.1.6",
"loader-utils": "^2.0.0",
"lodash": "^4.17.15",
"loud-rejection": "^2.2.0"
"loud-rejection": "^2.2.0",
"@formatjs/intl-utils": "^3.2.0"
},
"bugs": {
"url": "https://github.com/formatjs/formatjs/issues"
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/extract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {ExtractionResult, OptionsSchema} from 'babel-plugin-react-intl';
import * as babel from '@babel/core';
import {warn, getStdinAsString} from './console_utils';
import {outputJSONSync} from 'fs-extra';
import {interpolateName} from 'loader-utils';
import {interpolateName} from '@formatjs/intl-utils';
import {IOptions as GlobOptions} from 'glob';

export type ExtractCLIOptions = Omit<ExtractOptions, 'overrideIdFn'> & {
Expand Down
3 changes: 3 additions & 0 deletions packages/intl-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,8 @@
"devDependencies": {
"@types/serialize-javascript": "^1.5.0",
"serialize-javascript": "^3.0.0"
},
"dependencies": {
"emojis-list": "^3.0.0"
}
}
1 change: 1 addition & 0 deletions packages/intl-utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,4 @@ export * from './number-types';
export * from './displaynames-types';
export {removeUnitNamespace} from './units';
export {invariant} from './invariant';
export {interpolateName} from './interpolate-name';
147 changes: 147 additions & 0 deletions packages/intl-utils/src/interpolate-name.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import * as path from 'path';
import {createHash, HexBase64Latin1Encoding} from 'crypto';
export interface LoaderContext {
resourceQuery?: string;
resourcePath?: string;
options?: {
customInterpolateName(
this: LoaderContext,
url: string,
name: string | NameFn,
options: Options
): string;
};
}

export interface Options {
context?: string;
content?: string;
regExp?: RegExp;
}

export type NameFn = (resourcePath?: string, resourceQuery?: string) => string;

function getHashDigest(
content: string,
hashType: string,
digestType: HexBase64Latin1Encoding,
length: number
) {
const hasher = createHash(hashType);
hasher.update(content);
return hasher.digest(digestType).slice(0, length);
}

export function interpolateName(
loaderContext: LoaderContext,
name: string | NameFn,
options: Options
) {
let filename;

const hasQuery =
loaderContext.resourceQuery && loaderContext.resourceQuery.length > 1;

if (typeof name === 'function') {
filename = name(
loaderContext.resourcePath,
hasQuery ? loaderContext.resourceQuery : undefined
);
} else {
filename = name || '[hash].[ext]';
}

const context = options.context;
const content = options.content;
const regExp = options.regExp;

let ext = 'bin';
let basename = 'file';
let directory = '';
let folder = '';
let query = '';

if (loaderContext.resourcePath) {
const parsed = path.parse(loaderContext.resourcePath);
let resourcePath = loaderContext.resourcePath;

if (parsed.ext) {
ext = parsed.ext.substr(1);
}

if (parsed.dir) {
basename = parsed.name;
resourcePath = parsed.dir + path.sep;
}

if (typeof context !== 'undefined') {
directory = path
.relative(context, resourcePath + '_')
.replace(/\\/g, '/')
.replace(/\.\.(\/)?/g, '_$1');
directory = directory.substr(0, directory.length - 1);
} else {
directory = resourcePath.replace(/\\/g, '/').replace(/\.\.(\/)?/g, '_$1');
}

if (directory.length === 1) {
directory = '';
} else if (directory.length > 1) {
folder = path.basename(directory);
}
}

if (loaderContext.resourceQuery && loaderContext.resourceQuery.length > 1) {
query = loaderContext.resourceQuery;

const hashIdx = query.indexOf('#');

if (hashIdx >= 0) {
query = query.substr(0, hashIdx);
}
}

let url = filename;

if (content) {
// Match hash template
url = url
// `hash` and `contenthash` are same in `loader-utils` context
// let's keep `hash` for backward compatibility
.replace(
/\[(?:([^:\]]+):)?(?:hash|contenthash)(?::([a-z]+\d*))?(?::(\d+))?\]/gi,
(_, hashType, digestType, maxLength) =>
getHashDigest(content, hashType, digestType, parseInt(maxLength, 10))
);
}

url = url
.replace(/\[ext\]/gi, () => ext)
.replace(/\[name\]/gi, () => basename)
.replace(/\[path\]/gi, () => directory)
.replace(/\[folder\]/gi, () => folder)
.replace(/\[query\]/gi, () => query);

if (regExp && loaderContext.resourcePath) {
const match = loaderContext.resourcePath.match(new RegExp(regExp));

match &&
match.forEach((matched, i) => {
url = url.replace(new RegExp('\\[' + i + '\\]', 'ig'), matched);
});
}

if (
typeof loaderContext.options === 'object' &&
typeof loaderContext.options.customInterpolateName === 'function'
) {
url = loaderContext.options.customInterpolateName.call(
loaderContext,
url,
name,
options
);
}

return url;
}
14 changes: 14 additions & 0 deletions packages/intl-utils/tests/interpolate-name.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import {interpolateName} from '../src/interpolate-name';
import {createHash} from 'crypto';
describe('interpolateName', function () {
it('should match native', function () {
const hasher = createHash('sha1');
const content = 'foo#bar';
hasher.update(content);
expect(
interpolateName({}, '[sha1:contenthash:base64:6]', {
content,
})
).toBe(hasher.digest('base64').slice(0, 6));
});
});
3 changes: 2 additions & 1 deletion packages/ts-transformer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"dependencies": {
"@types/loader-utils": "^1.1.3",
"loader-utils": "^2.0.0",
"typescript": "3.8"
"typescript": "3.8",
"@formatjs/intl-utils": "^3.2.0"
},
"author": "Long Ho <holevietlong@gmail.com>",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion packages/ts-transformer/src/transform.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as ts from 'typescript';
import {MessageDescriptor} from './types';
import {interpolateName} from 'loader-utils';
import {interpolateName} from '@formatjs/intl-utils';

export type Extractor = (filePath: string, msgs: MessageDescriptor[]) => void;

Expand Down
2 changes: 1 addition & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4486,7 +4486,7 @@ classnames@^2.2.5, classnames@^2.2.6:
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==

cldr-core@^36.0.0:
cldr-core@36.0.0, cldr-core@^36.0.0:
version "36.0.0"
resolved "https://registry.yarnpkg.com/cldr-core/-/cldr-core-36.0.0.tgz#1d2148ed6802411845baeeb21432d7bbfde7d4f7"
integrity sha512-QLnAjt20rZe38c8h8OJ9jPND+O4o5O8Nw0TK/P3KpNn1cmOhMu0rk6Kc3ap96c5OStQ9gAngs9+Be2sum26NOw==
Expand Down

0 comments on commit 4d09912

Please sign in to comment.