-
Notifications
You must be signed in to change notification settings - Fork 77
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Migrate @fluent/bundle to TypeScript #436
Changes from 47 commits
5a3ba52
b0fb0b0
0c7adcd
dd1b458
5a44fce
3b010e9
2093617
242acc1
a804eba
3a5bf23
78a1aed
cbff094
a661351
8c7650f
1536a4b
a1b4479
3517b5a
22c0298
32a0788
847273e
7811060
cbb7ceb
2b958e8
2a88b88
86ce29b
b9fad68
6389bac
9d54272
0e10db2
7994277
c9a2766
2429087
51725b6
681366e
83c0c99
dbe37dd
cabdb9a
6128600
189a996
fa2b5f3
f1f69a3
3eeb319
af4d3b4
bd5ba8a
03ea7a0
4d48ced
cad6b9b
3218d81
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
esm/ | ||
index.js | ||
compat.js |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
.nyc_output | ||
coverage | ||
docs | ||
esm/.compiled | ||
src | ||
test | ||
makefile | ||
tsconfig.json |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
{ | ||
"parser": "@typescript-eslint/parser", | ||
"parserOptions": { | ||
"ecmaVersion": 9, | ||
"sourceType": "module", | ||
"project": "./tsconfig.json" | ||
}, | ||
"env": { | ||
"es6": true | ||
}, | ||
"extends": [ | ||
"eslint:recommended", | ||
"plugin:@typescript-eslint/eslint-recommended", | ||
"plugin:@typescript-eslint/recommended", | ||
"plugin:@typescript-eslint/recommended-requiring-type-checking", | ||
"../eslint_src.json" | ||
], | ||
"rules": { | ||
"prefer-const": "off", | ||
"no-unused-vars": ["error", {"args": "none"}], | ||
"@typescript-eslint/no-inferrable-types": "off", | ||
"@typescript-eslint/no-unused-vars": ["error", {"args": "none"}], | ||
"@typescript-eslint/no-use-before-define": "off", | ||
"@typescript-eslint/explicit-function-return-type": "error" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
export type Message = { | ||
id: string; | ||
value: Pattern | null; | ||
attributes: Record<string, Pattern>; | ||
}; | ||
|
||
export type Term = { | ||
id: string; | ||
value: Pattern; | ||
attributes: Record<string, Pattern>; | ||
}; | ||
|
||
export type Pattern = string | ComplexPattern; | ||
|
||
export type ComplexPattern = Array<PatternElement>; | ||
|
||
export type PatternElement = string | Expression; | ||
|
||
export type Expression = | ||
| SelectExpression | ||
| VariableReference | ||
| TermReference | ||
| MessageReference | ||
| FunctionReference | ||
| Literal; | ||
|
||
export type SelectExpression = { | ||
type: "select"; | ||
selector: Expression; | ||
variants: Array<Variant>; | ||
star: number; | ||
}; | ||
|
||
export type VariableReference = { | ||
type: "var"; | ||
name: string; | ||
}; | ||
|
||
export type TermReference = { | ||
type: "term"; | ||
name: string; | ||
attr: string | null; | ||
args: Array<Expression | NamedArgument>; | ||
}; | ||
|
||
export type MessageReference = { | ||
type: "mesg"; | ||
name: string; | ||
attr: string | null; | ||
}; | ||
|
||
export type FunctionReference = { | ||
type: "func"; | ||
name: string; | ||
args: Array<Expression | NamedArgument>; | ||
}; | ||
|
||
export type Variant = { | ||
key: Literal; | ||
value: Pattern; | ||
}; | ||
|
||
export type NamedArgument = { | ||
type: "narg"; | ||
name: string; | ||
value: Literal; | ||
}; | ||
|
||
export type Literal = StringLiteral | NumberLiteral; | ||
|
||
export type StringLiteral = { | ||
type: "str"; | ||
value: string; | ||
}; | ||
|
||
export type NumberLiteral = { | ||
type: "num"; | ||
value: number; | ||
precision: number; | ||
}; |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
/** | ||
* @overview | ||
* | ||
* The FTL resolver ships with a number of functions built-in. | ||
* | ||
* Each function take two arguments: | ||
* - args - an array of positional args | ||
* - opts - an object of key-value args | ||
* | ||
* Arguments to functions are guaranteed to already be instances of | ||
* `FluentValue`. Functions must return `FluentValues` as well. | ||
*/ | ||
|
||
import { | ||
FluentValue, | ||
FluentNone, | ||
FluentNumber, | ||
FluentDateTime | ||
} from "./types.js"; | ||
|
||
function values(opts: Record<string, FluentValue>): Record<string, unknown> { | ||
const unwrapped: Record<string, unknown> = {}; | ||
for (const [name, opt] of Object.entries(opts)) { | ||
unwrapped[name] = opt.valueOf(); | ||
} | ||
return unwrapped; | ||
} | ||
|
||
export function NUMBER( | ||
args: Array<FluentValue>, | ||
opts: Record<string, FluentValue> | ||
): FluentValue { | ||
let arg = args[0]; | ||
|
||
if (arg instanceof FluentNone) { | ||
return new FluentNone(`NUMBER(${arg.valueOf()})`); | ||
} | ||
|
||
if (arg instanceof FluentNumber || arg instanceof FluentDateTime) { | ||
return new FluentNumber(arg.valueOf(), { ...arg.opts, ...values(opts) }); | ||
} | ||
|
||
throw new TypeError("Invalid argument to NUMBER"); | ||
} | ||
|
||
export function DATETIME( | ||
args: Array<FluentValue>, | ||
opts: Record<string, FluentValue> | ||
): FluentValue { | ||
let arg = args[0]; | ||
|
||
if (arg instanceof FluentNone) { | ||
return new FluentNone(`DATETIME(${arg.valueOf()})`); | ||
} | ||
|
||
if (arg instanceof FluentNumber || arg instanceof FluentDateTime) { | ||
return new FluentDateTime(arg.valueOf(), { ...arg.opts, ...values(opts) }); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm having second thoughts about this. In TypeScript, this works because On a higher level, this is a spec question: what should be the outcome of I'll revert the |
||
} | ||
|
||
throw new TypeError("Invalid argument to DATETIME"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately, the amount of changes in this file makes diff think it's a new file. Here's what
NUMBER
used to look like:This code didn't compile in TS because
arg.opts
doesn't exist onFluentType
; it's only defined onFluentNumber
andFluentDateTime
. BTW the JS code works because{...undefined}
evaluates to{}
.In the TS rewrite, I introduced explicit
arg instanceof FluentNumber || arg instanceof FluentDateTime
guards and dropped theNumber()
parsing. This changes the current behavior of@fluent/bundle
: callingNUMBER
on a string-typed variable used to parse the string as a number and format it. After this change, the argument will be rejected as invalid.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I filed https://bugzilla.mozilla.org/show_bug.cgi?id=1605062 to prepare for this change in
mozilla-central
.