RORK LABJP
BUILD — Rork Max runs real Macs in the cloud loaded with Xcode and the iOS SDK, writing SwiftUI, compiling, reading the errors and building again. That loop, not the code generation, is what lifts the outputNATIVE — What comes out is pure Swift and SwiftUI, not React Native. Reaching AR, Metal graphics and widgets that React Native cannot touch is the real gap between this and other buildersPLATFORMS — Coverage spans iPhone, iPad, Apple Watch, Apple TV and Vision Pro, plus iMessage. Worth a look if you want to start from a watch app or an extension rather than a phone screenCOMPANION — The Rork Companion app lets you check a generated build on a real iPhone without a paid Apple Developer account, lowering the bar for trying a first project end to endPRICING — Free to start, paid plans from $25 a month, and Rork Max on the $200 Max plan. Worth working out up front how many projects it takes to earn that backDEADLINE — From August 31, 2026, Google Play requires target API level 36 or higher for new apps and updates alike. Ten days out, and the targetSdkVersion of what you generate is yours to verifyBUILD — Rork Max runs real Macs in the cloud loaded with Xcode and the iOS SDK, writing SwiftUI, compiling, reading the errors and building again. That loop, not the code generation, is what lifts the outputNATIVE — What comes out is pure Swift and SwiftUI, not React Native. Reaching AR, Metal graphics and widgets that React Native cannot touch is the real gap between this and other buildersPLATFORMS — Coverage spans iPhone, iPad, Apple Watch, Apple TV and Vision Pro, plus iMessage. Worth a look if you want to start from a watch app or an extension rather than a phone screenCOMPANION — The Rork Companion app lets you check a generated build on a real iPhone without a paid Apple Developer account, lowering the bar for trying a first project end to endPRICING — Free to start, paid plans from $25 a month, and Rork Max on the $200 Max plan. Worth working out up front how many projects it takes to earn that backDEADLINE — From August 31, 2026, Google Play requires target API level 36 or higher for new apps and updates alike. Ten days out, and the targetSdkVersion of what you generate is yours to verify
Articles/Dev Tools
Dev Tools/2026-08-14Intermediate

Your lockfile's dev/prod split won't tell you which licenses your app must credit

A record of classifying every dependency in a production project to decide what belongs on an app's license screen, and where the copyleft findings and the actual shipped artifact turned out to disagree.

Rork539Expo175LicensingOpen SourceApp Store87Indie Dev38

Premium Article

I stalled for half a day over whether to put a "Licenses" row at the bottom of a settings screen.

Adding it was clearly the right thing. What I could not decide was what to list there. A project exported by an AI builder carries hundreds of packages you never picked yourself. Do you list all of them, or only what actually ships? Pasting a template without answering that question is not disclosure. It is the appearance of disclosure.

So I classified the lockfile of a Next.js 16 project I run in production. The short version: the fourteen LGPL packages that made my stomach drop were not shipping at all. And the dependencies that genuinely needed crediting could not be identified from the dev / prod flags alone.

Start by counting the lockfile as it is

package-lock.json at lockfileVersion: 3 records a license field per package. You can inventory every dependency straight from the lockfile, without expanding node_modules.

Here is the script I actually ran. Standard library only.

# license_inventory.py - classify dependency licenses from the lockfile alone
import json, collections, sys
 
path = sys.argv[1] if len(sys.argv) > 1 else "package-lock.json"
with open(path, encoding="utf-8") as f:
    data = json.load(f)
 
packages = data.get("packages", {})
if not packages:
    # lockfileVersion 1 has no "packages" map, so fail loudly here
    raise SystemExit("No packages map. Regenerate the lockfile at version 2 or later.")
 
def classify(meta):
    # Order matters. Check dev first, or devOptional leaks into prod.
    if meta.get("dev"):
        return "dev"
    if meta.get("devOptional"):
        return "devOptional"
    if meta.get("optional"):
        return "optional"
    return "prod"
 
counts = collections.Counter()
by_class = collections.defaultdict(collections.Counter)
unknown = []
 
for name, meta in packages.items():
    if not name:          # the empty key is the root project itself
        continue
    kind = classify(meta)
    counts[kind] += 1
    lic = meta.get("license")
    if isinstance(lic, list):     # older packages sometimes use an array
        lic = " / ".join(lic)
    if lic is None:
        unknown.append(name)
        lic = "(none recorded)"
    by_class[kind][lic] += 1
 
print("total:", sum(counts.values()), dict(counts))
for kind in ("prod", "optional", "dev", "devOptional"):
    if by_class[kind]:
        print(f"--- {kind} ---")
        for lic, n in by_class[kind].most_common():
            print(f"  {n:4d}  {lic}")
print("missing license field:", len(unknown))
for name in unknown[:20]:
    print("  ", name)

The output looked like this.

ClassCountWhat it means
prod430Resolved as a production dependency
dev429Used only by builds, tests, and linters
optional70Platform-specific binaries and similar, installed conditionally
devOptional3Development-side and optional

932 packages in total. prod and dev split almost evenly at 46% each, and the entries carrying copyleft terms came to 27 packages, or 2.9% of the tree. Not one package was missing a license field.

That last part surprised me. With several hundred dependencies I expected a handful of gaps, and there were none. Even at the scale an indie developer works at, the provenance of the tree is tidier than I assumed.

Every copyleft hit was build-only

Scanning the breakdown, the first thing that caught my eye was ten packages under LGPL-3.0-or-later plus four more under Apache-2.0 AND LGPL-3.0-or-later. Fourteen in total. Enough to stop working and start reading.

Listing them showed all fourteen were platform binaries for the image library sharp (@img/sharp-libvips-*). All fourteen classified as dev.

The same pattern held for the thirteen MPL-2.0 packages: lightningcss with its platform binaries, plus the accessibility checker axe-core. Every one of them dev.

LicenseCountClassWhat they actually are
LGPL-3.0-or-later (alone and in combination)14all devsharp's libvips binaries
MPL-2.013all devlightningcss / axe-core
CC-BY-4.01prodcaniuse-lite

The fourteen packages I braced myself for never reach a user's device. The image conversion happened on a build machine, and only the converted output is distributed.

That points at something about method. If you begin a license inventory as a hunt for scary names, the first things you find will almost always be build tooling. Build tools run on developer machines, which makes them likely to ship native binaries, and native binaries are the most likely place for copyleft to appear. Searching in that order spends your attention exactly where it matters least.

MPL-2.0 asks that when you distribute covered software in executable form, you tell recipients how to obtain the corresponding source form (Mozilla Public License 2.0). Read the other way around: if you are not distributing it, that clause is not your problem yet. Establishing what you distribute has to come first.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
You will be able to separate dependencies that reach your shipped artifact from the ones that only run on your build machine, and decide on your own terms what belongs on a license screen
You will have a checking order that stops you from freezing the moment you spot a copyleft package name, before confirming whether it ever reaches users at all
You will see how 932 dependencies in a production project broke down by class, so you know where to look first in your own project
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

Dev Tools2026-08-20
A beta-SDK build can reach TestFlight, but it can't reach review
Builds made with a beta Xcode can be distributed through TestFlight, but they cannot be submitted for App Store review. Here is how to check which SDK produced your build, and how to protect your release profile in eas.json.
Dev Tools2026-07-17
Killing the Export Compliance Prompt in Rork Builds for Good
Every Rork and Rork Max build lands in App Store Connect with a Missing Compliance warning. Here is how to decide whether you qualify for the exemption, and how to set it once in app.json or Info.plist so the question never returns.
Dev Tools2026-06-27
Before a Free Preview Walks Out via Screenshot: Detecting Screenshots and Screen Recording in Rork/Expo
How to protect paid preview images from screenshots and screen recording in a Rork/Expo app: the limits of expo-screen-capture, native isCaptured monitoring, and an iOS/Android-aware blur design.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →