@formatjs/ts-transformer 3.9.9 → 3.9.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BUILD +82 -0
- package/CHANGELOG.md +713 -0
- package/LICENSE.md +0 -0
- package/README.md +0 -0
- package/examples/compile.ts +50 -0
- package/index.ts +3 -0
- package/integration-tests/BUILD +37 -0
- package/integration-tests/integration/comp.tsx +40 -0
- package/integration-tests/integration/jest.config.js +25 -0
- package/integration-tests/integration/ts-jest.test.tsx +32 -0
- package/integration-tests/package.json +5 -0
- package/integration-tests/vue/fixtures/index.vue +30 -0
- package/integration-tests/vue/fixtures/main.ts +4 -0
- package/integration-tests/vue/integration.test.ts +75 -0
- package/package.json +3 -3
- package/src/console_utils.ts +32 -0
- package/src/interpolate-name.ts +147 -0
- package/src/transform.ts +764 -0
- package/src/types.ts +12 -0
- package/tests/__snapshots__/index.test.ts.snap +908 -0
- package/tests/fixtures/FormattedMessage.tsx +35 -0
- package/tests/fixtures/additionalComponentNames.tsx +16 -0
- package/tests/fixtures/additionalFunctionNames.tsx +20 -0
- package/tests/fixtures/ast.tsx +83 -0
- package/tests/fixtures/defineMessages.tsx +67 -0
- package/tests/fixtures/defineMessagesPreserveWhitespace.tsx +87 -0
- package/tests/fixtures/descriptionsAsObjects.tsx +17 -0
- package/tests/fixtures/extractFromFormatMessage.tsx +45 -0
- package/tests/fixtures/extractFromFormatMessageStateless.tsx +46 -0
- package/tests/fixtures/extractSourceLocation.tsx +8 -0
- package/tests/fixtures/formatMessageCall.tsx +44 -0
- package/tests/fixtures/inline.tsx +26 -0
- package/tests/fixtures/nested.tsx +10 -0
- package/tests/fixtures/noImport.tsx +52 -0
- package/tests/fixtures/overrideIdFn.tsx +70 -0
- package/tests/fixtures/removeDefaultMessage.tsx +22 -0
- package/tests/fixtures/removeDescription.tsx +22 -0
- package/tests/fixtures/resourcePath.tsx +23 -0
- package/tests/fixtures/stringConcat.tsx +26 -0
- package/tests/fixtures/templateLiteral.tsx +21 -0
- package/tests/index.test.ts +127 -0
- package/tests/interpolate-name.test.ts +14 -0
- package/ts-jest-integration.ts +9 -0
- package/tsconfig.json +5 -0
- package/index.d.ts +0 -4
- package/index.d.ts.map +0 -1
- package/index.js +0 -6
- package/src/console_utils.d.ts +0 -4
- package/src/console_utils.d.ts.map +0 -1
- package/src/console_utils.js +0 -49
- package/src/interpolate-name.d.ts +0 -15
- package/src/interpolate-name.d.ts.map +0 -1
- package/src/interpolate-name.js +0 -95
- package/src/transform.d.ts +0 -78
- package/src/transform.d.ts.map +0 -1
- package/src/transform.js +0 -483
- package/src/types.d.ts +0 -12
- package/src/types.d.ts.map +0 -1
- package/src/types.js +0 -2
- package/ts-jest-integration.d.ts +0 -6
- package/ts-jest-integration.d.ts.map +0 -1
- package/ts-jest-integration.js +0 -10
package/LICENSE.md
CHANGED
|
File without changes
|
package/README.md
CHANGED
|
File without changes
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import * as ts from 'typescript'
|
|
2
|
+
import {transform as intlTransformer} from '../'
|
|
3
|
+
|
|
4
|
+
declare module 'fs-extra' {
|
|
5
|
+
export function outputJsonSync(file: string, data: any, opts?: {}): void
|
|
6
|
+
}
|
|
7
|
+
const CJS_CONFIG: ts.CompilerOptions = {
|
|
8
|
+
experimentalDecorators: true,
|
|
9
|
+
jsx: ts.JsxEmit.React,
|
|
10
|
+
module: ts.ModuleKind.CommonJS,
|
|
11
|
+
moduleResolution: ts.ModuleResolutionKind.NodeJs,
|
|
12
|
+
noEmitOnError: false,
|
|
13
|
+
noUnusedLocals: true,
|
|
14
|
+
noUnusedParameters: true,
|
|
15
|
+
stripInternal: true,
|
|
16
|
+
declaration: true,
|
|
17
|
+
baseUrl: __dirname,
|
|
18
|
+
target: ts.ScriptTarget.ES2015,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default function compile(
|
|
22
|
+
input: string,
|
|
23
|
+
compilerOptions: ts.CompilerOptions = CJS_CONFIG
|
|
24
|
+
) {
|
|
25
|
+
const compilerHost = ts.createCompilerHost(compilerOptions)
|
|
26
|
+
const program = ts.createProgram([input], compilerOptions, compilerHost)
|
|
27
|
+
|
|
28
|
+
const msgs = {}
|
|
29
|
+
|
|
30
|
+
let emitResult = program.emit(undefined, undefined, undefined, undefined, {
|
|
31
|
+
before: [
|
|
32
|
+
intlTransformer({
|
|
33
|
+
overrideIdFn: '[hash:base64:10]',
|
|
34
|
+
}),
|
|
35
|
+
],
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
let allDiagnostics = ts
|
|
39
|
+
.getPreEmitDiagnostics(program)
|
|
40
|
+
.concat(emitResult.diagnostics)
|
|
41
|
+
console.log(
|
|
42
|
+
ts.formatDiagnosticsWithColorAndContext(allDiagnostics, {
|
|
43
|
+
getCanonicalFileName: fileName => fileName,
|
|
44
|
+
getCurrentDirectory: () => process.cwd(),
|
|
45
|
+
getNewLine: () => ts.sys.newLine,
|
|
46
|
+
})
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
return msgs
|
|
50
|
+
}
|
package/index.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
load("@npm//:defs.bzl", "npm_link_all_packages")
|
|
2
|
+
load("//tools:jest.bzl", "jest_test")
|
|
3
|
+
|
|
4
|
+
npm_link_all_packages(name = "node_modules")
|
|
5
|
+
|
|
6
|
+
jest_test(
|
|
7
|
+
name = "integration",
|
|
8
|
+
srcs = glob([
|
|
9
|
+
"integration/*.ts*",
|
|
10
|
+
]),
|
|
11
|
+
jest_config = "integration/jest.config.js",
|
|
12
|
+
deps = [
|
|
13
|
+
":node_modules/@formatjs/ts-transformer",
|
|
14
|
+
"//:node_modules/@types/react",
|
|
15
|
+
"//:node_modules/@types/react-dom",
|
|
16
|
+
"//:node_modules/react",
|
|
17
|
+
"//:node_modules/react-dom",
|
|
18
|
+
],
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
jest_test(
|
|
22
|
+
name = "integration-vue",
|
|
23
|
+
srcs = ["vue/integration.test.ts"] + glob([
|
|
24
|
+
"vue/fixtures/*",
|
|
25
|
+
]),
|
|
26
|
+
flaky = True,
|
|
27
|
+
deps = [
|
|
28
|
+
":node_modules/@formatjs/ts-transformer",
|
|
29
|
+
"//:node_modules/@types/node",
|
|
30
|
+
"//:node_modules/@types/webpack",
|
|
31
|
+
"//:node_modules/ts-loader",
|
|
32
|
+
"//:node_modules/vue",
|
|
33
|
+
"//:node_modules/vue-class-component",
|
|
34
|
+
"//:node_modules/vue-loader",
|
|
35
|
+
"//:node_modules/webpack",
|
|
36
|
+
],
|
|
37
|
+
)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
|
|
3
|
+
const intl = {
|
|
4
|
+
formatMessage: (desc: any) => desc,
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function FormattedMessage(props: {
|
|
8
|
+
id?: string
|
|
9
|
+
defaultMessage: string
|
|
10
|
+
description?: string
|
|
11
|
+
children?: any
|
|
12
|
+
}) {
|
|
13
|
+
return (
|
|
14
|
+
<span>
|
|
15
|
+
{props.id} - {JSON.stringify(props.defaultMessage)} - {props.description}{' '}
|
|
16
|
+
- {props.children('fooo')}
|
|
17
|
+
</span>
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function defineMessage(any: any): any {
|
|
22
|
+
return any
|
|
23
|
+
}
|
|
24
|
+
export const msg = defineMessage({
|
|
25
|
+
defaultMessage: 'defineMessage',
|
|
26
|
+
description: 'foo',
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
export const Component = (): React.ReactElement => {
|
|
30
|
+
return (
|
|
31
|
+
<FormattedMessage defaultMessage="test message">
|
|
32
|
+
{(chunks: any) => chunks}
|
|
33
|
+
</FormattedMessage>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const formattedMessage = intl.formatMessage({
|
|
38
|
+
defaultMessage: 'formatMessage',
|
|
39
|
+
description: 'foo',
|
|
40
|
+
})
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
console.log(process.cwd())
|
|
2
|
+
module.exports = {
|
|
3
|
+
preset: 'ts-jest',
|
|
4
|
+
testEnvironment: 'jsdom',
|
|
5
|
+
globals: {
|
|
6
|
+
'ts-jest': {
|
|
7
|
+
tsconfig: 'tsconfig.json',
|
|
8
|
+
astTransformers: {
|
|
9
|
+
before: [
|
|
10
|
+
{
|
|
11
|
+
path: require.resolve(
|
|
12
|
+
'@formatjs/ts-transformer/ts-jest-integration'
|
|
13
|
+
),
|
|
14
|
+
options: {
|
|
15
|
+
// options
|
|
16
|
+
overrideIdFn: '[sha512:contenthash:base64:6]',
|
|
17
|
+
ast: true,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
verbose: true,
|
|
25
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import {msg, Component, formattedMessage} from './comp'
|
|
2
|
+
import * as React from 'react'
|
|
3
|
+
import * as ReactDOMServer from 'react-dom/server'
|
|
4
|
+
describe('ts-jest transformer', function () {
|
|
5
|
+
it('should work with ts-jest', function () {
|
|
6
|
+
expect(msg).toEqual({
|
|
7
|
+
defaultMessage: [
|
|
8
|
+
{
|
|
9
|
+
type: 0,
|
|
10
|
+
value: 'defineMessage',
|
|
11
|
+
},
|
|
12
|
+
],
|
|
13
|
+
id: 'Vg+BA7',
|
|
14
|
+
})
|
|
15
|
+
})
|
|
16
|
+
it('intl.formatMessage', function () {
|
|
17
|
+
expect(formattedMessage).toEqual({
|
|
18
|
+
defaultMessage: [
|
|
19
|
+
{
|
|
20
|
+
type: 0,
|
|
21
|
+
value: 'formatMessage',
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
id: 'w8L3pO',
|
|
25
|
+
})
|
|
26
|
+
})
|
|
27
|
+
it('Comp', function () {
|
|
28
|
+
expect(ReactDOMServer.renderToString(<Component />)).toEqual(
|
|
29
|
+
'<span data-reactroot="">lQsqfv<!-- --> - <!-- -->[{"type":0,"value":"test message"}]<!-- --> - <!-- --> <!-- -->- <!-- -->fooo</span>'
|
|
30
|
+
)
|
|
31
|
+
})
|
|
32
|
+
})
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<p>
|
|
3
|
+
Index
|
|
4
|
+
{{ $formatMessage({defaultMessage: 'test message (id not injected)'}) }}
|
|
5
|
+
</p>
|
|
6
|
+
</template>
|
|
7
|
+
|
|
8
|
+
<script lang="ts">
|
|
9
|
+
import {inject} from 'vue'
|
|
10
|
+
import {Vue} from 'vue-class-component'
|
|
11
|
+
|
|
12
|
+
class Index extends Vue {
|
|
13
|
+
public created() {
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
15
|
+
const intl = inject('intl')!
|
|
16
|
+
|
|
17
|
+
console.log(
|
|
18
|
+
// @ts-ignore
|
|
19
|
+
intl.formatMessage({defaultMessage: 'message in created (id injected)'})
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default Index
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<script lang="ts" setup>
|
|
28
|
+
const intl = useIntl()
|
|
29
|
+
console.log(intl.formatMessage({defaultMessage: 'script setup'}))
|
|
30
|
+
</script>
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import webpack from 'webpack'
|
|
2
|
+
import {VueLoaderPlugin} from 'vue-loader'
|
|
3
|
+
import {readFileSync} from 'fs'
|
|
4
|
+
import {join} from 'path'
|
|
5
|
+
import {transform} from '@formatjs/ts-transformer'
|
|
6
|
+
test('tranpilation', function (done) {
|
|
7
|
+
webpack(
|
|
8
|
+
{
|
|
9
|
+
entry: require.resolve('./fixtures/main.ts'),
|
|
10
|
+
output: {
|
|
11
|
+
path: __dirname,
|
|
12
|
+
filename: 'out.js',
|
|
13
|
+
},
|
|
14
|
+
module: {
|
|
15
|
+
rules: [
|
|
16
|
+
{
|
|
17
|
+
test: /\.vue$/,
|
|
18
|
+
loader: 'vue-loader',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
test: /\.[t|j]s$/u,
|
|
22
|
+
use: [
|
|
23
|
+
{
|
|
24
|
+
loader: 'ts-loader',
|
|
25
|
+
options: {
|
|
26
|
+
appendTsSuffixTo: [/\.vue$/u],
|
|
27
|
+
getCustomTransformers() {
|
|
28
|
+
return {
|
|
29
|
+
before: [
|
|
30
|
+
transform({
|
|
31
|
+
overrideIdFn: '[sha512:contenthash:base64:6]',
|
|
32
|
+
}),
|
|
33
|
+
],
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
transpileOnly: true,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
plugins: [new VueLoaderPlugin()],
|
|
44
|
+
},
|
|
45
|
+
(err, stats) => {
|
|
46
|
+
expect(err).toBeNull()
|
|
47
|
+
const statsJson = stats?.toJson()
|
|
48
|
+
if (!statsJson) {
|
|
49
|
+
throw new Error('missing stats')
|
|
50
|
+
}
|
|
51
|
+
if (err) {
|
|
52
|
+
throw err
|
|
53
|
+
}
|
|
54
|
+
if (stats?.hasErrors()) {
|
|
55
|
+
console.error(statsJson.errors)
|
|
56
|
+
throw new Error('err compiling')
|
|
57
|
+
}
|
|
58
|
+
const outFile = join(
|
|
59
|
+
statsJson.outputPath || __dirname,
|
|
60
|
+
statsJson.assets?.[0].name || 'out.js'
|
|
61
|
+
)
|
|
62
|
+
const outFileContent = readFileSync(outFile, 'utf-8')
|
|
63
|
+
expect(outFileContent).toContain(
|
|
64
|
+
'.formatMessage({id:"XOeJ9m",defaultMessage:"message in created (id injected)"}))}'
|
|
65
|
+
)
|
|
66
|
+
expect(outFileContent).toContain(
|
|
67
|
+
'$formatMessage({id:"GuoEHM",defaultMessage:"test message (id not injected)"}))'
|
|
68
|
+
)
|
|
69
|
+
expect(outFileContent).toContain(
|
|
70
|
+
'.formatMessage({id:"S3wEt4",defaultMessage:"script setup"}))'
|
|
71
|
+
)
|
|
72
|
+
done()
|
|
73
|
+
}
|
|
74
|
+
)
|
|
75
|
+
}, 30000)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@formatjs/ts-transformer",
|
|
3
|
-
"version": "3.9.
|
|
3
|
+
"version": "3.9.10",
|
|
4
4
|
"description": "TS Compiler transformer for formatjs",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"react-intl"
|
|
18
18
|
],
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@formatjs/icu-messageformat-parser": "2.1.
|
|
20
|
+
"@formatjs/icu-messageformat-parser": "2.1.5",
|
|
21
21
|
"@types/json-stable-stringify": "^1.0.32",
|
|
22
22
|
"@types/node": "14 || 16 || 17",
|
|
23
23
|
"chalk": "^4.0.0",
|
|
@@ -39,4 +39,4 @@
|
|
|
39
39
|
"url": "https://github.com/formatjs/formatjs/issues"
|
|
40
40
|
},
|
|
41
41
|
"homepage": "https://github.com/formatjs/formatjs#readme"
|
|
42
|
-
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import {green, red, yellow} from 'chalk'
|
|
2
|
+
import {format} from 'util'
|
|
3
|
+
|
|
4
|
+
const LEVEL_COLORS = {
|
|
5
|
+
debug: green,
|
|
6
|
+
warn: yellow,
|
|
7
|
+
error: red,
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function label(level: keyof typeof LEVEL_COLORS, message: string) {
|
|
11
|
+
return `[@formatjs/ts-transformer] [${LEVEL_COLORS[level](
|
|
12
|
+
level.toUpperCase()
|
|
13
|
+
)}] ${message}`
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function debug(message: string, ...args: any[]) {
|
|
17
|
+
if (process.env.LOG_LEVEL !== 'debug') {
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
console.error(format(label('debug', message), ...args))
|
|
21
|
+
console.error('\n')
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function warn(message: string, ...args: any[]): void {
|
|
25
|
+
console.error(format(label('warn', message), ...args))
|
|
26
|
+
console.error('\n')
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function error(message: string, ...args: any[]): void {
|
|
30
|
+
console.error(format(label('error', message), ...args))
|
|
31
|
+
console.error('\n')
|
|
32
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import * as path from 'path'
|
|
2
|
+
import {BinaryToTextEncoding, createHash} from 'crypto'
|
|
3
|
+
export interface LoaderContext {
|
|
4
|
+
resourceQuery?: string
|
|
5
|
+
resourcePath?: string
|
|
6
|
+
options?: {
|
|
7
|
+
customInterpolateName(
|
|
8
|
+
this: LoaderContext,
|
|
9
|
+
url: string,
|
|
10
|
+
name: string | NameFn,
|
|
11
|
+
options: Options
|
|
12
|
+
): string
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface Options {
|
|
17
|
+
context?: string
|
|
18
|
+
content?: string
|
|
19
|
+
regExp?: RegExp
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type NameFn = (resourcePath?: string, resourceQuery?: string) => string
|
|
23
|
+
|
|
24
|
+
function getHashDigest(
|
|
25
|
+
content: string,
|
|
26
|
+
hashType = 'md5',
|
|
27
|
+
digestType: BinaryToTextEncoding = 'hex',
|
|
28
|
+
length = 9999
|
|
29
|
+
) {
|
|
30
|
+
const hasher = createHash(hashType)
|
|
31
|
+
hasher.update(content)
|
|
32
|
+
return hasher.digest(digestType).slice(0, length)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function interpolateName(
|
|
36
|
+
loaderContext: LoaderContext,
|
|
37
|
+
name: string | NameFn,
|
|
38
|
+
options: Options
|
|
39
|
+
) {
|
|
40
|
+
let filename
|
|
41
|
+
|
|
42
|
+
const hasQuery =
|
|
43
|
+
loaderContext.resourceQuery && loaderContext.resourceQuery.length > 1
|
|
44
|
+
|
|
45
|
+
if (typeof name === 'function') {
|
|
46
|
+
filename = name(
|
|
47
|
+
loaderContext.resourcePath,
|
|
48
|
+
hasQuery ? loaderContext.resourceQuery : undefined
|
|
49
|
+
)
|
|
50
|
+
} else {
|
|
51
|
+
filename = name || '[hash].[ext]'
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const context = options.context
|
|
55
|
+
const content = options.content
|
|
56
|
+
const regExp = options.regExp
|
|
57
|
+
|
|
58
|
+
let ext = 'bin'
|
|
59
|
+
let basename = 'file'
|
|
60
|
+
let directory = ''
|
|
61
|
+
let folder = ''
|
|
62
|
+
let query = ''
|
|
63
|
+
|
|
64
|
+
if (loaderContext.resourcePath) {
|
|
65
|
+
const parsed = path.parse(loaderContext.resourcePath)
|
|
66
|
+
let resourcePath = loaderContext.resourcePath
|
|
67
|
+
|
|
68
|
+
if (parsed.ext) {
|
|
69
|
+
ext = parsed.ext.slice(1)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (parsed.dir) {
|
|
73
|
+
basename = parsed.name
|
|
74
|
+
resourcePath = parsed.dir + path.sep
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (typeof context !== 'undefined') {
|
|
78
|
+
directory = path
|
|
79
|
+
.relative(context, resourcePath + '_')
|
|
80
|
+
.replace(/\\/g, '/')
|
|
81
|
+
.replace(/\.\.(\/)?/g, '_$1')
|
|
82
|
+
directory = directory.slice(0, -1)
|
|
83
|
+
} else {
|
|
84
|
+
directory = resourcePath.replace(/\\/g, '/').replace(/\.\.(\/)?/g, '_$1')
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (directory.length === 1) {
|
|
88
|
+
directory = ''
|
|
89
|
+
} else if (directory.length > 1) {
|
|
90
|
+
folder = path.basename(directory)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (loaderContext.resourceQuery && loaderContext.resourceQuery.length > 1) {
|
|
95
|
+
query = loaderContext.resourceQuery
|
|
96
|
+
|
|
97
|
+
const hashIdx = query.indexOf('#')
|
|
98
|
+
|
|
99
|
+
if (hashIdx >= 0) {
|
|
100
|
+
query = query.slice(0, hashIdx)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
let url = filename
|
|
105
|
+
|
|
106
|
+
if (content) {
|
|
107
|
+
// Match hash template
|
|
108
|
+
url = url
|
|
109
|
+
// `hash` and `contenthash` are same in `loader-utils` context
|
|
110
|
+
// let's keep `hash` for backward compatibility
|
|
111
|
+
.replace(
|
|
112
|
+
/\[(?:([^:\]]+):)?(?:hash|contenthash)(?::([a-z]+\d*))?(?::(\d+))?\]/gi,
|
|
113
|
+
(_, hashType, digestType, maxLength) =>
|
|
114
|
+
getHashDigest(content, hashType, digestType, parseInt(maxLength, 10))
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
url = url
|
|
119
|
+
.replace(/\[ext\]/gi, () => ext)
|
|
120
|
+
.replace(/\[name\]/gi, () => basename)
|
|
121
|
+
.replace(/\[path\]/gi, () => directory)
|
|
122
|
+
.replace(/\[folder\]/gi, () => folder)
|
|
123
|
+
.replace(/\[query\]/gi, () => query)
|
|
124
|
+
|
|
125
|
+
if (regExp && loaderContext.resourcePath) {
|
|
126
|
+
const match = loaderContext.resourcePath.match(new RegExp(regExp))
|
|
127
|
+
|
|
128
|
+
match &&
|
|
129
|
+
match.forEach((matched, i) => {
|
|
130
|
+
url = url.replace(new RegExp('\\[' + i + '\\]', 'ig'), matched)
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (
|
|
135
|
+
typeof loaderContext.options === 'object' &&
|
|
136
|
+
typeof loaderContext.options.customInterpolateName === 'function'
|
|
137
|
+
) {
|
|
138
|
+
url = loaderContext.options.customInterpolateName.call(
|
|
139
|
+
loaderContext,
|
|
140
|
+
url,
|
|
141
|
+
name,
|
|
142
|
+
options
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return url
|
|
147
|
+
}
|