Skip to content
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

Implement <fbt:list>. #18

Merged
merged 1 commit into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ const { existsSync, readFileSync } = require('node:fs');

module.exports = {
extends: ['@nkzw'],
ignorePatterns: ['packages/*/lib'],
ignorePatterns: ['packages/*/lib', 'packages/fbtee/lib-tmp/'],
overrides: [
{
files: ['**/__tests__/**/*.tsx'],
files: [
'./packages/babel-plugin-fbtee/src/bin/*.tsx',
'./packages/fbtee/babel-build.config.js',
'**/__tests__/**/*.tsx',
],
rules: {
'no-console': 0,
'workspaces/no-relative-imports': 0,
Expand All @@ -29,6 +33,7 @@ module.exports = {
devDependencies: [
'./example/vite.config.ts',
'./jest-preprocessor.js',
'./packages/fbtee/babel-build.config.js',
'**/__tests__/**/*.tsx',
],
packageDir: [__dirname].concat(
Expand Down
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ node_modules
packages/*/lib
packages/*/LICENSE
packages/*/README.md
packages/fbt/lib
packages/fbt/LICENSE
packages/fbtee/.enum_manifest.json
packages/fbtee/.src_manifest.json
packages/fbtee/lib-tmp
packages/fbtee/Strings.json
tsconfig.tsbuildinfo
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ example/source_strings.json
example/src/translatedFbts
example/src/translatedFbts.json
packages/*/lib/
packages/fbtee/lib-tmp/
pnpm-lock.yaml
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ The open-source version of `fbt`, however, became unmaintained, difficult to set
- **Easier Setup:** fbtee works with modern tools like Vite.
- **Statically Typed:** The fbtee compiler ensures correct usage of fbtee, libary TypeScript types are provided, and an eslint plugin helps fix common mistakes.
- **Improved React Compatibility:** Removed React-specific hacks and added support for implicit React fragments (`<>`).
- **Enhanced Features:** Fixed and exported `intlList`, which was not functional in the original `fbt`.
- **Enhanced Features:** Fixed and exported `inltList` as a new `<fbt:list>` construt, which was not functional in the original `fbt`.
- **Modernized Codebase:** Rewritten using TypeScript, ES modules (ESM), eslint, and modern JavaScript standards. Removed cruft and legacy code.
- **Updated Tooling:** Uses modern tools like pnpm, Vite, and esbuild for faster and more efficient development of **fbtee**.

Expand Down
24 changes: 22 additions & 2 deletions example/src/example/Example.react.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,6 @@ export default function Example() {
</span>
</span>
</fieldset>

<fieldset>
<span className="sentence example_row">
<fbt desc="example 1">
Expand All @@ -202,7 +201,6 @@ export default function Example() {
</fbt>
</span>
</fieldset>

<fieldset>
<span className={classNames('example_row', 'example_row--multi')}>
<span
Expand Down Expand Up @@ -287,6 +285,28 @@ export default function Example() {
</fbt>
</span>
</fieldset>
<fieldset>
<label>
<fbt desc="List example.">
Do you want to share a{' '}
<fbt:list
conjunction="or"
items={[
<fbt desc="Item in a list." key="photo">
photo
</fbt>,
<fbt desc="Item in a list." key="photo">
link
</fbt>,
<fbt desc="Item in a list." key="video">
video
</fbt>,
]}
name="list"
/>?
</fbt>
</label>
</fieldset>
<fieldset>
<span className="example_row">
<button
Expand Down
27 changes: 27 additions & 0 deletions example/src/example/__tests__/CollectedString-test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { test } from '@jest/globals';
import { CollectFbtOutput } from '../../../../packages/babel-plugin-fbtee/src/bin/collect.tsx';

const { childParentMappings, phrases } = JSON.parse(
readFileSync(
join(import.meta.dirname, '../../../.source_strings.json'),
'utf8',
),
) as CollectFbtOutput;
test('fbtee strings are included in the collected strings of the example project', () => {
expect(
phrases.some(
({ filepath }) => filepath === 'node_modules/fbtee/lib/index.js',
),
).toBe(true);
});
test('fbtee child-parent mappings are included in the collected strings of the example project', () => {
expect(childParentMappings).toMatchInlineSnapshot(`
{
"14": 13,
"23": 22,
"24": 23,
}
`);
});
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,11 @@ exports[`Example.react renders the example 1`] = `
their link.
</span>
</fieldset>
<fieldset>
<label>
Do you want to share a photo, link or video?
</label>
</fieldset>
<fieldset>
<span
class="example_row"
Expand Down
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
"type": "module",
"scripts": {
"build": "pnpm -r build",
"build:all": "pnpm -r build && pnpm install && pnpm copy-files && cd example && pnpm build:fbtee",
"clean": "rm -rf packages/*/lib; cd example pnpm clean",
"copy-files": "find packages/* -type d -maxdepth 0 -exec cp README.md LICENSE {} \\;",
"build:all": "pnpm -r build && pnpm install && pnpm --filter=fbtee build:fbtee-strings && pnpm copy-files && cd example && pnpm build:fbtee",
"clean": "rm -rf packages/*/lib packages/fbtee/lib-tmp; cd example pnpm clean",
"copy-files": "find packages/* -type d -maxdepth 0 -exec cp README.md LICENSE Strings.json {} \\;",
"dev": "cd example && pnpm build:fbtee && pnpm dev",
"format": "prettier --write .",
"jest": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules --no-warnings\" node_modules/.bin/jest",
Expand All @@ -19,10 +19,12 @@
"tsc:check": "tsc"
},
"devDependencies": {
"@babel/cli": "^7.26.4",
"@babel/core": "^7.26.0",
"@babel/generator": "^7.26.3",
"@babel/parser": "^7.26.3",
"@babel/plugin-syntax-import-attributes": "^7.26.0",
"@babel/plugin-syntax-typescript": "^7.25.9",
"@babel/preset-react": "^7.26.3",
"@babel/preset-typescript": "^7.26.0",
"@babel/types": "^7.26.3",
Expand Down
5 changes: 1 addition & 4 deletions packages/babel-plugin-fbtee/src/FbtConstants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,7 @@ export const FbtBooleanOptions = {
} as const;

export const CommonOption = 'common';
export const FbtCallMustHaveAtLeastOneOfTheseAttributes = new Set([
'desc',
CommonOption,
]);
export const RequiredFbtAttributes = new Set(['desc', CommonOption]);

export const FbtRequiredAttributes = {
desc: true,
Expand Down
4 changes: 0 additions & 4 deletions packages/babel-plugin-fbtee/src/FbtNodeChecker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -178,10 +178,6 @@ export default class FbtNodeChecker {
: null;
}

/**
* This is same as the non-static getFbtConstructNameFromFunctionCall except
* it accepts any of the three fbt modules (`FBT`, `FBS`).
*/
static getFbtNodeTypeFromFunctionCall(node: Node): FbtNodeType | null {
return (
(isCallExpression(node) &&
Expand Down
23 changes: 13 additions & 10 deletions packages/babel-plugin-fbtee/src/FbtUtil.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ import {
JSXElement,
JSXExpressionContainer,
JSXFragment,
JSXOpeningElement,
JSXSpreadAttribute,
JSXText,
memberExpression,
Expand Down Expand Up @@ -62,8 +61,6 @@ import nullthrows from './nullthrows.tsx';

const { hasOwnProperty } = Object.prototype;

type JSXAttributes = ReadonlyArray<JSXOpeningElement['attributes'][number]>;

export type CallExpressionArg =
| Expression
| SpreadElement
Expand Down Expand Up @@ -130,6 +127,10 @@ export function checkOption<K extends string>(
): K {
const optionName = option as K;

if (optionName === 'key') {
return optionName;
}
Comment on lines +130 to +132
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Allows usage of key on fbt, like if you are using fbt in an array.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we start tracking changes / new features in a CHANGELOG file to facilitate assembling release notes?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably, yeah.


const validValues = validOptions[optionName];
if (!hasOwnProperty.call(validOptions, optionName) || validValues == null) {
throw errorAt(
Expand Down Expand Up @@ -506,27 +507,29 @@ const isJSXAttributeWithValue = (
): node is JSXAttributeWithValue => node.value != null;

export function getAttributeByNameOrThrow(
attributes: JSXAttributes,
node: JSXElement,
name: string,
node: Node | null = null,
): JSXAttributeWithValue {
const attribute = getAttributeByName(attributes, name);
const attribute = getAttributeByName(node, name);
if (attribute == null) {
throw errorAt(node, `Unable to find attribute "${name}".`);
throw errorAt(node, `This node requires a '${name}' attribute.`);
}

if (!isJSXAttributeWithValue(attribute)) {
throw errorAt(node, `Attribute "${name}" has no value.`);
throw errorAt(
node,
`This '${name}' attribute of this node requires a value.`,
);
}

return attribute;
}

export function getAttributeByName(
attributes: JSXAttributes,
node: JSXElement,
name: string,
): JSXAttribute | null {
for (const attribute of attributes) {
for (const attribute of node.openingElement.attributes) {
if (isJSXAttribute(attribute) && attribute.name.name === name) {
return attribute;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`<fbt:list> 1`] = `
import { fbt } from "fbtee";
const x = fbt._(
/* __FBT__ start */ {
jsfbt: {
m: [],
t: { desc: "Lists", text: "Available Locations: {locations}." },
},
project: "",
} /* __FBT__ end */,
[fbt._list("locations", ["Tokyo", "London", "Vienna"])]
);
`;

exports[`<fbt:list> 2`] = `
import { fbt } from "fbtee";
const x = fbt._(
/* __FBT__ start */ {
jsfbt: {
m: [],
t: { desc: "Lists", text: "Available Locations: {locations}." },
},
project: "",
} /* __FBT__ end */,
[fbt._list("locations", ["Tokyo", "London", "Vienna"], "and")]
);
`;

exports[`<fbt:list> 3`] = `
import { fbt } from "fbtee";
const x = fbt._(
/* __FBT__ start */ {
jsfbt: {
m: [],
t: { desc: "Lists", text: "Available Locations: {locations}." },
},
project: "",
} /* __FBT__ end */,
[fbt._list("locations", ["Tokyo", "London", "Vienna"], null, "bullet")]
);
`;

exports[`<fbt:list> 4`] = `
import { fbt } from "fbtee";
const x = fbt._(
/* __FBT__ start */ {
jsfbt: {
m: [],
t: { desc: "Lists", text: "Available Locations: {locations}." },
},
project: "",
} /* __FBT__ end */,
[fbt._list("locations", ["Tokyo", "London", "Vienna"], "or", "bullet")]
);
`;

exports[`fbt.list() 1`] = `
import { fbt } from "fbtee";
fbt._(
/* __FBT__ start */ {
jsfbt: {
m: [],
t: { desc: "Lists", text: "Available Locations: {locations}" },
},
project: "",
} /* __FBT__ end */,
[fbt._list("locations", ["Tokyo", "London", "Vienna"], null, "or")]
);
`;
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Test Fbt Enum should handle functional enums (with references) (import default) 1`] = `
import { fbt } from "fbt";
import { fbt } from "fbtee";
import aEnum from "Test$FbtEnum";
var x = fbt._(
/* __FBT__ start */ {
Expand All @@ -21,7 +21,7 @@ var x = fbt._(
`;

exports[`Test Fbt Enum should handle functional enums (with references) (import star) 1`] = `
import { fbt } from "fbt";
import { fbt } from "fbtee";
import * as aEnum from "Test$FbtEnum";
var x = fbt._(
/* __FBT__ start */ {
Expand Down
Loading
Loading