From cc6e6b7667a4ecb103375861dfa88dd2484cbfd2 Mon Sep 17 00:00:00 2001 From: vocksel Date: Mon, 1 Jan 2024 16:10:50 -0800 Subject: [PATCH] Hoist wally types and fix type errors (#233) # Problem Discovered when hoisting up types for Wally packages that there's several type errors to resolve # Solution This PR adds `wally-package-types` and a new `bin/wally-install.sh` script for installing packages and hoisting up types at the same time. All of this is integrated into CI, and relevant analysis errors have been patched # Checklist - [ ] Ran `./bin/test.sh` locally before merging --- .github/workflows/ci.yml | 15 +++--------- .vscode/tasks.json | 8 ++++++- bin/analyze.sh | 3 +++ bin/wally-install.sh | 9 ++++++++ foreman.toml | 1 + src/Explorer/Component/init.lua | 8 ++++--- src/Forms/Button.lua | 14 ++++++++---- src/Forms/InputField.lua | 8 ++++--- src/Forms/Searchbar.lua | 8 ++++--- src/Panels/DragHandle.lua | 8 ++++--- src/Panels/ResizablePanel.lua | 8 ++++--- src/Panels/Sidebar.story.lua | 8 +++++-- src/Plugin/PluginApp.story.lua | 1 + src/Storybook/StoryControls.lua | 3 ++- src/Storybook/StoryControls.story.lua | 1 + src/Storybook/StoryMeta.lua | 2 +- src/Storybook/StoryPreview.lua | 8 ++++--- src/Storybook/loadStoryModule.lua | 20 ++++++++++++---- src/Storybook/types.lua | 33 ++++++++++++++++++++++----- 19 files changed, 115 insertions(+), 51 deletions(-) create mode 100755 bin/wally-install.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b2f1c0b4..59189268 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,19 +51,10 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} - name: Install dependencies - run: wally install - - - name: Download global Roblox types - shell: bash - run: curl -s -O https://raw.githubusercontent.com/JohnnyMorganz/luau-lsp/master/scripts/globalTypes.d.lua - - - name: Generate sourcemap for LSP - shell: bash - run: rojo sourcemap tests.project.json -o sourcemap.json + run: ./bin/wally-install.sh - - name: Analyze - shell: bash - run: luau-lsp analyze --sourcemap=sourcemap.json --defs=globalTypes.d.lua --defs=testez.d.lua --ignore=**/_Index/** src/ + - name: Run Luau analysis + run: ./bin/analyze.sh scripts: runs-on: ubuntu-latest diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 707502f0..93ae4924 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -3,6 +3,12 @@ // for the documentation about the tasks.json format "version": "2.0.0", "tasks": [ + { + "label": "Install packages", + "type": "shell", + "problemMatcher": [], + "command": "./bin/wally-install.sh" + }, { "label": "Build (Development)", "type": "shell", @@ -50,4 +56,4 @@ "command": "npx moonwave dev" } ] -} +} \ No newline at end of file diff --git a/bin/analyze.sh b/bin/analyze.sh index ef235e9f..27d530d4 100755 --- a/bin/analyze.sh +++ b/bin/analyze.sh @@ -5,5 +5,8 @@ curl -s -O https://raw.githubusercontent.com/JohnnyMorganz/luau-lsp/master/scrip rojo sourcemap tests.project.json -o sourcemap.json luau-lsp analyze --sourcemap=sourcemap.json --defs=globalTypes.d.lua --defs=testez.d.lua --ignore=**/_Index/** src/ +exit_code=$? rm globalTypes.d.lua + +exit $exit_code diff --git a/bin/wally-install.sh b/bin/wally-install.sh new file mode 100755 index 00000000..3e6b7ec5 --- /dev/null +++ b/bin/wally-install.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -e + +wally install + +rojo sourcemap tests.project.json --output sourcemap.json + +wally-package-types --sourcemap sourcemap.json Packages/ diff --git a/foreman.toml b/foreman.toml index 98b7b143..1d7400a1 100644 --- a/foreman.toml +++ b/foreman.toml @@ -7,3 +7,4 @@ tarmac = { source = "Roblox/tarmac", version = "0.7.0" } wally = { source = "UpliftGames/wally", version = "0.3.2" } moonwave-extractor = { source = "UpliftGames/moonwave", version = "1.0.0" } luau-lsp = { source = "JohnnyMorganz/luau-lsp", version = "1.x" } +wally-package-types = { source = "JohnnyMorganz/wally-package-types", version = "1.2.1" } diff --git a/src/Explorer/Component/init.lua b/src/Explorer/Component/init.lua index 72b361e6..b85b5fbf 100644 --- a/src/Explorer/Component/init.lua +++ b/src/Explorer/Component/init.lua @@ -13,15 +13,17 @@ local defaultProps = { indent = 0, } -type Props = typeof(defaultProps) & { +type Props = { node: types.ComponentTreeNode, filter: string?, activeNode: types.ComponentTreeNode?, onClick: ((types.ComponentTreeNode) -> ())?, } -local function Component(props: Props) - props = Sift.Dictionary.merge(defaultProps, props) +type InternalProps = Props & typeof(defaultProps) + +local function Component(providedProps: Props) + local props: InternalProps = Sift.Dictionary.merge(defaultProps, providedProps) local hasChildren = props.node.children and #props.node.children > 0 diff --git a/src/Forms/Button.lua b/src/Forms/Button.lua index 78d21ae2..df5c1b25 100644 --- a/src/Forms/Button.lua +++ b/src/Forms/Button.lua @@ -11,14 +11,16 @@ local function shift(color: Color3, percent: number): Color3 return Color3.fromHSV(h, s, math.clamp(v * (1 + percent), 0, 1)) end +type Style = "contain" | "stroke" + local defaultProps = { - style = "contain", + style = "contain" :: Style, text = "Button", } -type Props = typeof(defaultProps) & { +type Props = { text: string, - style: "contain" | "stroke", + style: Style?, anchorPoint: Vector2?, position: UDim2?, @@ -29,8 +31,10 @@ type Props = typeof(defaultProps) & { onClick: (() -> ())?, } -local function Button(props: Props) - props = Sift.Dictionary.join(defaultProps, props) +type InternalProps = Props & typeof(defaultProps) + +local function Button(providedProps: Props) + local props: InternalProps = Sift.Dictionary.join(defaultProps, providedProps) local theme = useTheme() local hover, setHover = React.useState(false) diff --git a/src/Forms/InputField.lua b/src/Forms/InputField.lua index 083c3255..17137b63 100644 --- a/src/Forms/InputField.lua +++ b/src/Forms/InputField.lua @@ -12,7 +12,7 @@ local defaultProps = { autoFocus = false, } -export type Props = typeof(defaultProps) & { +export type Props = { layoutOrder: number?, onSubmit: (text: string) -> (), onFocus: (() -> ())?, @@ -22,8 +22,10 @@ export type Props = typeof(defaultProps) & { transform: ((newText: string, oldText: string) -> string)?, } -local function InputField(props: Props) - props = Sift.Dictionary.merge(defaultProps, props) +type InternalProps = Props & typeof(defaultProps) + +local function InputField(providedProps: Props) + local props: InternalProps = Sift.Dictionary.merge(defaultProps, providedProps) local ref = React.createRef() local text, setText = React.useState("") diff --git a/src/Forms/Searchbar.lua b/src/Forms/Searchbar.lua index 5fc12818..f14ecc29 100644 --- a/src/Forms/Searchbar.lua +++ b/src/Forms/Searchbar.lua @@ -16,15 +16,17 @@ local defaultProps = { size = UDim2.new(1, 0, 0, 36), } -type Props = typeof(defaultProps) & { +export type Props = { layoutOrder: number?, onSearchChanged: ((value: string) -> ())?, } +type InternalProps = Props & typeof(defaultProps) + local SEARCH_ICON_SIZE = 16 -- px -local function Searchbar(props: Props) - props = Sift.Dictionary.merge(defaultProps, props) +local function Searchbar(providedProps: Props) + local props: InternalProps = Sift.Dictionary.merge(defaultProps, providedProps) local theme = useTheme() local search, setSearch = React.useState("") diff --git a/src/Panels/DragHandle.lua b/src/Panels/DragHandle.lua index ba376625..c3e6f02c 100644 --- a/src/Panels/DragHandle.lua +++ b/src/Panels/DragHandle.lua @@ -13,14 +13,16 @@ local defaultProps = { hoverIconY = "rbxasset://textures/StudioUIEditor/icon_resize4.png", } -export type Props = typeof(defaultProps) & { +export type Props = { handle: types.DragHandle, onDrag: (delta: Vector2) -> (), onDragEnd: (() -> ())?, } -local function DragHandle(props: Props) - props = Sift.Dictionary.merge(defaultProps, props) +type InternalProps = Props & typeof(defaultProps) + +local function DragHandle(providedProps: Props) + local props: InternalProps = Sift.Dictionary.merge(defaultProps, providedProps) local plugin = React.useContext(PluginContext.Context) local isDragging, setIsDragging = React.useState(false) diff --git a/src/Panels/ResizablePanel.lua b/src/Panels/ResizablePanel.lua index dc02860a..ac5441d4 100644 --- a/src/Panels/ResizablePanel.lua +++ b/src/Panels/ResizablePanel.lua @@ -11,7 +11,7 @@ local defaultProps = { maxSize = Vector2.new(math.huge, math.huge), } -export type Props = typeof(defaultProps) & { +export type Props = { initialSize: UDim2, layoutOrder: number?, dragHandles: { types.DragHandle }?, @@ -21,8 +21,10 @@ export type Props = typeof(defaultProps) & { children: any, } -local function ResizablePanel(props: Props) - props = Sift.Dictionary.merge(defaultProps, props) +type InternalProps = Props & typeof(defaultProps) + +local function ResizablePanel(providedProps: Props) + local props: InternalProps = Sift.Dictionary.merge(defaultProps, providedProps) local absoluteSize, setAbsoluteSize = React.useState(nil) diff --git a/src/Panels/Sidebar.story.lua b/src/Panels/Sidebar.story.lua index 87959062..b332bb49 100644 --- a/src/Panels/Sidebar.story.lua +++ b/src/Panels/Sidebar.story.lua @@ -11,7 +11,11 @@ return { storybooks = { internalStorybook, }, - selectStory = print, - selectStorybook = print, + selectStory = function(storyModule) + print(storyModule) + end, + selectStorybook = function(storybook) + print(storybook) + end, }), } diff --git a/src/Plugin/PluginApp.story.lua b/src/Plugin/PluginApp.story.lua index c109df28..32aef06e 100644 --- a/src/Plugin/PluginApp.story.lua +++ b/src/Plugin/PluginApp.story.lua @@ -9,5 +9,6 @@ return { controls = {}, story = React.createElement(PluginApp, { loader = ModuleLoader.new(), + plugin = plugin, }), } diff --git a/src/Storybook/StoryControls.lua b/src/Storybook/StoryControls.lua index 5c171301..b0d55ce1 100644 --- a/src/Storybook/StoryControls.lua +++ b/src/Storybook/StoryControls.lua @@ -10,9 +10,9 @@ local Dropdown = require(flipbook.Forms.Dropdown) local e = React.createElement type Props = { - layoutOrder: number, controls: { [string]: any }, setControl: (key: string, value: any) -> (), + layoutOrder: number?, } local function StoryControls(props: Props) @@ -40,6 +40,7 @@ local function StoryControls(props: Props) option = React.createElement(InputField, { placeholder = value, onTextChange = setControl, + onSubmit = setControl, }) end diff --git a/src/Storybook/StoryControls.story.lua b/src/Storybook/StoryControls.story.lua index 5cc1c70d..92e808f4 100644 --- a/src/Storybook/StoryControls.story.lua +++ b/src/Storybook/StoryControls.story.lua @@ -15,5 +15,6 @@ return { "Option 3", }, }, + setControl = function() end, }), } diff --git a/src/Storybook/StoryMeta.lua b/src/Storybook/StoryMeta.lua index 763cfbc2..e937cbe1 100644 --- a/src/Storybook/StoryMeta.lua +++ b/src/Storybook/StoryMeta.lua @@ -9,8 +9,8 @@ local MAX_SUMMARY_SIZE = 600 local e = React.createElement export type Props = { - layoutOrder: number, story: types.Story, + layoutOrder: number?, } local function StoryMeta(props: Props) diff --git a/src/Storybook/StoryPreview.lua b/src/Storybook/StoryPreview.lua index 8b129421..86e709e8 100644 --- a/src/Storybook/StoryPreview.lua +++ b/src/Storybook/StoryPreview.lua @@ -17,7 +17,7 @@ local defaultProps = { zoom = 0, } -type Props = typeof(defaultProps) & { +export type Props = { layoutOrder: number, story: types.Story, ref: any, @@ -25,8 +25,10 @@ type Props = typeof(defaultProps) & { storyModule: ModuleScript, } -local StoryPreview = React.forwardRef(function(props: Props, ref: any) - props = Sift.Dictionary.merge(defaultProps, props) +type InternalProps = Props & typeof(defaultProps) + +local StoryPreview = React.forwardRef(function(providedProps: Props, ref: any) + local props: InternalProps = Sift.Dictionary.merge(defaultProps, providedProps) local err, setErr = React.useState(nil) diff --git a/src/Storybook/loadStoryModule.lua b/src/Storybook/loadStoryModule.lua index 319a4529..cd17902a 100644 --- a/src/Storybook/loadStoryModule.lua +++ b/src/Storybook/loadStoryModule.lua @@ -31,13 +31,23 @@ local function loadStoryModule(loader: any, module: ModuleScript, storybook: typ local isValid, message = types.StoryMeta(result) if isValid then + local extraProps = {} + if types.ReactStorybook(storybook) then + local reactStorybook = storybook :: types.ReactStorybook + extraProps = { + react = reactStorybook.react, + reactRoblox = reactStorybook.reactRoblox, + } + elseif types.RoactStorybook(storybook) then + local roactStorybook = storybook :: types.RoactStorybook + extraProps = { + roact = roactStorybook.roact, + } + end + story = Sift.Dictionary.merge({ name = module.Name, - }, { - react = storybook.react, - reactRoblox = storybook.reactRoblox, - roact = storybook.roact, - }, result) + }, extraProps, result) else return nil, Errors.Generic:format(module:GetFullName(), message) end diff --git a/src/Storybook/types.lua b/src/Storybook/types.lua index dd478e0c..31734422 100644 --- a/src/Storybook/types.lua +++ b/src/Storybook/types.lua @@ -41,13 +41,9 @@ export type StoryProps = { controls: Controls, } -export type Storybook = { +export type StorybookMeta = { storyRoots: { Instance }, - name: string?, - roact: Roact?, - react: React?, - reactRoblox: ReactRoblox?, } types.Storybook = t.interface({ storyRoots = t.array(t.Instance), @@ -58,8 +54,33 @@ types.Storybook = t.interface({ reactRoblox = t.optional(types.ReactRoblox), }) +export type RoactStorybook = StorybookMeta & { + roact: Roact, +} +types.RoactStorybook = t.union( + types.Storybook, + t.interface({ + roact = t.optional(types.Roact), + }) +) + +export type ReactStorybook = StorybookMeta & { + react: React, + reactRoblox: ReactRoblox, +} +types.ReactStorybook = t.union( + types.Storybook, + t.interface({ + react = t.optional(types.React), + reactRoblox = t.optional(types.ReactRoblox), + }) +) + +export type Storybook = RoactStorybook | ReactStorybook | StorybookMeta + export type StoryMeta = { name: string, + story: any, summary: string?, controls: Controls?, roact: Roact?, @@ -90,6 +111,6 @@ export type FunctionalStory = StoryMeta & { story: (target: GuiObject, props: StoryProps) -> (() -> ())?, } -export type Story = FunctionalStory | RoactStory | ReactStory +export type Story = FunctionalStory | RoactStory | ReactStory | StoryMeta return types