Skip to content
This repository has been archived by the owner on Dec 13, 2023. It is now read-only.

Commit

Permalink
Setup basic Luau analysis (#372)
Browse files Browse the repository at this point in the history
This sets up the repo with Luau analysis and fixes the initial type errors.
  • Loading branch information
ghostnaps authored Jun 10, 2022
1 parent 2909222 commit 82b805a
Show file tree
Hide file tree
Showing 16 changed files with 88 additions and 23 deletions.
21 changes: 21 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,24 @@ on:
- master

jobs:
analyze:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2

- uses: Roblox/setup-foreman@v1
with:
version: "^1.0.1"
token: ${{ secrets.GITHUB_TOKEN }}

- name: Download global Roblox types
shell: bash
run: curl -O https://raw.githubusercontent.com/JohnnyMorganz/luau-analyze-rojo/master/globalTypes.d.lua

- name: Analyze
shell: bash
run: luau-analyze --project=default.project.json --defs=globalTypes.d.lua --defs=testez.d.lua src/

test:
runs-on: ubuntu-latest

Expand Down Expand Up @@ -52,6 +70,9 @@ jobs:
luarocks install luacov
luarocks install luacov-reporter-lcov
- name: Install and run darklua
run: darklua process src/ src/ --format retain-lines

- name: Test
run: |
lua -lluacov bin/spec.lua
Expand Down
7 changes: 7 additions & 0 deletions .luaurc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"languageMode": "nonstrict",
"lint": {
"*": true
},
"lintErrors": true
}
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Unreleased Changes
* Removed the warning for `setState` on unmounted components to eliminate false positive warnings, matching upstream React ([#323](https://github.com/Roblox/roact/pull/323)).
* Added Luau analysis to the repository ([#372](https://github.com/Roblox/roact/pull/372))

## [1.4.2](https://github.com/Roblox/roact/releases/tag/v1.4.2) (October 6th, 2021)
* Fixed forwardRef doc code referencing React instead of Roact ([#310](https://github.com/Roblox/roact/pull/310)).
Expand Down
6 changes: 4 additions & 2 deletions foreman.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
[tools]
rojo = { source = "rojo-rbx/rojo", version = "6.2.0" }
selene = { source = "Kampfkarren/selene", version = "0.14" }
stylua = { source = "JohnnyMorganz/StyLua", version = "0.11" }
selene = { source = "Kampfkarren/selene", version = "0.18" }
stylua = { source = "JohnnyMorganz/StyLua", version = "0.13" }
luau-analyze = { source = "JohnnyMorganz/luau-analyze-rojo", version = "0.527" }
darklua = { gitlab = "seaofvoices/darklua", version = "0.7.0" }
2 changes: 1 addition & 1 deletion src/Binding.lua
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ function BindingInternalApi.join(upstreamBindings)
disconnect()
end

disconnects = nil
disconnects = nil :: any
end
end

Expand Down
1 change: 1 addition & 0 deletions src/Component.lua
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ function Component:__mount(reconciler, virtualNode)
virtualNode = virtualNode,
componentClass = self,
lifecyclePhase = ComponentLifecyclePhase.Init,
pendingState = nil,
}

local instance = {
Expand Down
9 changes: 6 additions & 3 deletions src/ElementUtils.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
--!strict
local Type = require(script.Parent.Type)
local Symbol = require(script.Parent.Symbol)

Expand All @@ -16,6 +17,8 @@ local ElementUtils = {}
]]
ElementUtils.UseParentKey = Symbol.named("UseParentKey")

type Iterator<K, V> = ({ [K]: V }, K?) -> (K?, V?)
type Element = { [any]: any }
--[[
Returns an iterator over the children of an element.
`elementOrElements` may be one of:
Expand All @@ -37,14 +40,14 @@ ElementUtils.UseParentKey = Symbol.named("UseParentKey")
If `elementOrElements` is none of the above, this function will throw.
]]
function ElementUtils.iterateElements(elementOrElements)
function ElementUtils.iterateElements<K>(elementOrElements): (Iterator<K, Element>, any, nil)
local richType = Type.of(elementOrElements)

-- Single child
if richType == Type.Element then
local called = false

return function()
return function(_, _)
if called then
return nil
else
Expand All @@ -57,7 +60,7 @@ function ElementUtils.iterateElements(elementOrElements)
local regularType = typeof(elementOrElements)

if elementOrElements == nil or regularType == "boolean" then
return noop
return (noop :: any) :: Iterator<K, Element>
end

if regularType == "table" then
Expand Down
2 changes: 1 addition & 1 deletion src/Symbol.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
--!nonstrict
--!strict
--[[
A 'Symbol' is an opaque marker type.
Expand Down
3 changes: 2 additions & 1 deletion src/Symbol.spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ return function()
it("should coerce to the given name", function()
local symbol = Symbol.named("foo")

expect(tostring(symbol):find("foo")).to.be.ok()
local index = tostring(symbol):find("foo")
expect(index).to.be.ok()
end)

it("should be unique when constructed", function()
Expand Down
13 changes: 7 additions & 6 deletions src/assertDeepEqual.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
--!strict
--[[
A utility used to assert that two objects are value-equal recursively. It
outputs fairly nicely formatted messages to help diagnose why two objects
Expand All @@ -6,7 +7,7 @@
This should only be used in tests.
]]

local function deepEqual(a, b)
local function deepEqual(a: any, b: any): (boolean, string?)
if typeof(a) ~= typeof(b) then
local message = ("{1} is of type %s, but {2} is of type %s"):format(typeof(a), typeof(b))
return false, message
Expand All @@ -19,7 +20,7 @@ local function deepEqual(a, b)
visitedKeys[key] = true

local success, innerMessage = deepEqual(value, b[key])
if not success then
if not success and innerMessage then
local message = innerMessage
:gsub("{1}", ("{1}[%s]"):format(tostring(key)))
:gsub("{2}", ("{2}[%s]"):format(tostring(key)))
Expand All @@ -32,7 +33,7 @@ local function deepEqual(a, b)
if not visitedKeys[key] then
local success, innerMessage = deepEqual(value, a[key])

if not success then
if not success and innerMessage then
local message = innerMessage
:gsub("{1}", ("{1}[%s]"):format(tostring(key)))
:gsub("{2}", ("{2}[%s]"):format(tostring(key)))
Expand All @@ -42,11 +43,11 @@ local function deepEqual(a, b)
end
end

return true
return true, nil
end

if a == b then
return true
return true, nil
end

local message = "{1} ~= {2}"
Expand All @@ -56,7 +57,7 @@ end
local function assertDeepEqual(a, b)
local success, innerMessageTemplate = deepEqual(a, b)

if not success then
if not success and innerMessageTemplate then
local innerMessage = innerMessageTemplate:gsub("{1}", "first"):gsub("{2}", "second")

local message = ("Values were not deep-equal.\n%s"):format(innerMessage)
Expand Down
2 changes: 1 addition & 1 deletion src/assign.spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ return function()
end)

it("should merge multiple tables onto the given target table", function()
local target = {
local target: { a: number, b: number, c: number? } = {
a = 5,
b = 6,
}
Expand Down
2 changes: 1 addition & 1 deletion src/createReconciler.lua
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ local function createReconciler(renderer)
mount a new virtual node, and return it in this case, while also issuing
a warning to the user.
]]
function updateVirtualNode(virtualNode, newElement, newState)
function updateVirtualNode(virtualNode, newElement, newState: { [any]: any }?): { [any]: any }?
if config.internalTypeChecks then
internalAssert(Type.of(virtualNode) == Type.VirtualNode, "Expected arg #1 to be of type VirtualNode")
end
Expand Down
4 changes: 3 additions & 1 deletion src/createReconciler.spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,9 @@ return function()
local node = noopReconciler.mountVirtualNode(emptyFragment, nil, "test")

expect(node).to.be.ok()
expect(next(node.children)).to.never.be.ok()

local nextNode = next(node.children)
expect(nextNode).to.never.be.ok()
end)

it("should mount all fragment's children", function()
Expand Down
2 changes: 1 addition & 1 deletion src/init.spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ return function()
end

if not success then
local existence = typeof(valueType) == "boolean" and "present" or "of type " .. valueType
local existence = typeof(valueType) == "boolean" and "present" or "of type " .. tostring(valueType)
local message = ("Expected public API member %q to be %s, but instead it was of type %s"):format(
tostring(key),
existence,
Expand Down
12 changes: 7 additions & 5 deletions src/strict.lua
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
--!nonstrict
local function strict(t, name)
name = name or tostring(t)
--!strict
local function strict(t: { [any]: any }, name: string?)
-- FIXME Luau: Need to define a new variable since reassigning `name = ...`
-- doesn't narrow the type
local newName = name or tostring(t)

return setmetatable(t, {
__index = function(_self, key)
local message = ("%q (%s) is not a valid member of %s"):format(tostring(key), typeof(key), name)
local message = ("%q (%s) is not a valid member of %s"):format(tostring(key), typeof(key), newName)

error(message, 2)
end,

__newindex = function(_self, key, _value)
local message = ("%q (%s) is not a valid member of %s"):format(tostring(key), typeof(key), name)
local message = ("%q (%s) is not a valid member of %s"):format(tostring(key), typeof(key), newName)

error(message, 2)
end,
Expand Down
24 changes: 24 additions & 0 deletions testez.d.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
declare function afterAll(callback: () -> ()): ()
declare function afterEach(callback: () -> ()): ()

declare function beforeAll(callback: () -> ()): ()
declare function beforeEach(callback: () -> ()): ()

declare function describe(phrase: string, callback: () -> ()): ()
declare function describeFOCUS(phrase: string, callback: () -> ()): ()
declare function fdescribe(phrase: string, callback: () -> ()): ()
declare function describeSKIP(phrase: string, callback: () -> ()): ()
declare function xdescribe(phrase: string, callback: () -> ()): ()

declare function expect(value: any): any

declare function FIXME(optionalMessage: string?): ()
declare function FOCUS(): ()
declare function SKIP(): ()

declare function it(phrase: string, callback: () -> ()): ()
declare function itFOCUS(phrase: string, callback: () -> ()): ()
declare function fit(phrase: string, callback: () -> ()): ()
declare function itSKIP(phrase: string, callback: () -> ()): ()
declare function xit(phrase: string, callback: () -> ()): ()
declare function itFIXME(phrase: string, callback: () -> ()): ()

0 comments on commit 82b805a

Please sign in to comment.