Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
3a1d4dd
chore(setup): add SDK/project detection library
ffantl-ld Jul 21, 2026
9efecc7
fix(setup): report whether the detected entry point exists
ffantl-ld Jul 31, 2026
2259349
fix(setup): keep the Next.js SDK key out of the browser bundle
ffantl-ld Jul 31, 2026
3fefe37
docs(setup): cite the sources for the entry-point candidates
ffantl-ld Jul 31, 2026
c276645
fix(setup): detect backend manifests before package.json
ffantl-ld Jul 31, 2026
0b61639
test(setup): assert the whole result per project shape
ffantl-ld Aug 3, 2026
b5d737c
fix(setup): find the src/main entry a Node app bootstraps from
ffantl-ld Aug 3, 2026
37bb50b
chore(setup): add SDK installer library
ffantl-ld Jul 21, 2026
6d3551a
fix(setup): install with the project's own package manager
ffantl-ld Jul 31, 2026
45e5847
fix(setup): match whole packages and target the right .NET project
ffantl-ld Aug 3, 2026
d2de7f2
chore(setup): add SDK init/injection library and templates
ffantl-ld Jul 21, 2026
0d6a927
fix(setup): correct the React Native init snippet
ffantl-ld Jul 31, 2026
519fe65
fix(setup): pass the required auto-env argument in mobile snippets
ffantl-ld Aug 3, 2026
8daf723
fix(setup): build the Swift context without try
ffantl-ld Aug 3, 2026
82b2cb3
Merge branch 'setup-ld' into ffantl/setup-ld/3-initializer
ffantl-ld Aug 4, 2026
9126132
fix(setup): keep the shebang first when injecting imports
ffantl-ld Aug 4, 2026
51ae9ec
fix(setup): inject node imports in the entry file's module syntax
ffantl-ld Aug 4, 2026
26827c0
fix(setup): keep leading directives above injected imports
ffantl-ld Aug 4, 2026
1d4cf46
fix(setup): treat block comments as part of the leading header
ffantl-ld Aug 4, 2026
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
484 changes: 484 additions & 0 deletions internal/setup/initializer.go

Large diffs are not rendered by default.

497 changes: 497 additions & 0 deletions internal/setup/initializer_test.go

Large diffs are not rendered by default.

31 changes: 31 additions & 0 deletions internal/setup/installer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -372,3 +372,34 @@ func TestCsprojFiles_SkipsBuildOutput(t *testing.T) {

assert.Equal(t, []string{filepath.Join(dir, "src/MyApp/MyApp.csproj")}, csprojFiles(dir))
}

// The templates import the package InstallArgs installs; a mismatch means the user
// installs one package and the snippet requires another. These are the pairs where
// LaunchDarkly ships both a scoped and an unscoped package for the same SDK.
func TestInstallArgs_PackageMatchesTemplateImport(t *testing.T) {
tests := []struct {
sdkID string
wantImport string
}{
{"node-server", "@launchdarkly/node-server-sdk"},
{"react-client-sdk", "launchdarkly-react-client-sdk"},
{"react-native", "@launchdarkly/react-native-client-sdk"},
{"js-client-sdk", "launchdarkly-js-client-sdk"},
}
for _, tt := range tests {
t.Run(tt.sdkID, func(t *testing.T) {
_, pkg := InstallArgs(tt.sdkID, "npm")
assert.Equal(t, tt.wantImport, pkg)

rendered, err := RenderTemplate(tt.sdkID, InitConfig{})
require.NoError(t, err)
assert.Contains(t, rendered, "'"+tt.wantImport+"'",
"template must import the package we install")

esm, err := RenderTemplateForEntry(tt.sdkID, "src/main.ts", InitConfig{})
require.NoError(t, err)
assert.Contains(t, esm, "'"+tt.wantImport+"'",
"ESM template must import the package we install")
})
}
}
11 changes: 11 additions & 0 deletions internal/setup/sdk_init_templates/android.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import com.launchdarkly.sdk.android.*;
import com.launchdarkly.sdk.android.LDConfig.Builder.AutoEnvAttributes;
import com.launchdarkly.sdk.*;
// --- init ---
LDConfig ldConfig = new LDConfig.Builder(AutoEnvAttributes.Enabled)
.mobileKey("{{.MobileKey}}")
.build();
Comment thread
cursor[bot] marked this conversation as resolved.
LDContext ldContext = LDContext.builder(ContextKind.DEFAULT, "example-user-key")
.name("Example User")
.build();
LDClient ldClient = LDClient.init(this.getApplication(), ldConfig, ldContext, 5);
11 changes: 11 additions & 0 deletions internal/setup/sdk_init_templates/dotnet-server-sdk.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using LaunchDarkly.Sdk;
using LaunchDarkly.Sdk.Server;
// --- init ---
var ldClient = new LdClient("{{.SDKKey}}");

var context = Context.Builder("example-user-key")
.Name("Example User")
.Build();

var flagValue = ldClient.BoolVariation("{{.FlagKey}}", context, false);
Console.WriteLine($"Flag '{{.FlagKey}}' is {flagValue}");
16 changes: 16 additions & 0 deletions internal/setup/sdk_init_templates/go-server-sdk.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import (
"fmt"
"time"

"github.com/launchdarkly/go-sdk-common/v3/ldcontext"
ld "github.com/launchdarkly/go-server-sdk/v7"
)
// --- init ---
ldClient, _ := ld.MakeClient("{{.SDKKey}}", 5*time.Second)

context := ldcontext.NewBuilder("example-user-key").
Name("Example User").
Build()

flagValue, _ := ldClient.BoolVariation("{{.FlagKey}}", context, false)
fmt.Printf("Flag '{{.FlagKey}}' is %t\n", flagValue)
11 changes: 11 additions & 0 deletions internal/setup/sdk_init_templates/java-server-sdk.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import com.launchdarkly.sdk.*;
import com.launchdarkly.sdk.server.*;
// --- init ---
LDClient ldClient = new LDClient("{{.SDKKey}}");

LDContext context = LDContext.builder("example-user-key")
.name("Example User")
.build();

boolean flagValue = ldClient.boolVariation("{{.FlagKey}}", context, false);
System.out.println("Flag '{{.FlagKey}}' is " + flagValue);
12 changes: 12 additions & 0 deletions internal/setup/sdk_init_templates/js-client-sdk.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import * as LDClient from 'launchdarkly-js-client-sdk';
Comment thread
cursor[bot] marked this conversation as resolved.
// --- init ---
const ldClient = LDClient.initialize('{{.ClientSideID}}', {
kind: 'user',
key: 'example-user-key',
name: 'Example User',
});

ldClient.on('ready', () => {
const flagValue = ldClient.variation('{{.FlagKey}}', false);
console.log(`Flag '{{.FlagKey}}' is ${flagValue}`);
});
15 changes: 15 additions & 0 deletions internal/setup/sdk_init_templates/node-server-esm.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import * as LaunchDarkly from '@launchdarkly/node-server-sdk';
// --- init ---
const ldClient = LaunchDarkly.init('{{.SDKKey}}');

const context = {
kind: 'user',
key: 'example-user-key',
name: 'Example User',
};

ldClient.on('ready', () => {
ldClient.variation('{{.FlagKey}}', context, false, (err, flagValue) => {
console.log(`Flag '{{.FlagKey}}' is ${flagValue}`);
});
});
15 changes: 15 additions & 0 deletions internal/setup/sdk_init_templates/node-server.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const LaunchDarkly = require('@launchdarkly/node-server-sdk');
Comment thread
cursor[bot] marked this conversation as resolved.
// --- init ---
const ldClient = LaunchDarkly.init('{{.SDKKey}}');

const context = {
kind: 'user',
key: 'example-user-key',
name: 'Example User',
};

ldClient.on('ready', () => {
ldClient.variation('{{.FlagKey}}', context, false, (err, flagValue) => {
console.log(`Flag '{{.FlagKey}}' is ${flagValue}`);
});
});
11 changes: 11 additions & 0 deletions internal/setup/sdk_init_templates/python-server-sdk.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import ldclient
from ldclient import Context
from ldclient.config import Config
# --- init ---
ldclient.set_config(Config("{{.SDKKey}}"))
ld_client = ldclient.get()

context = Context.builder("example-user-key").name("Example User").build()

flag_value = ld_client.variation("{{.FlagKey}}", context, False)
print(f"Flag '{{.FlagKey}}' is {flag_value}")
10 changes: 10 additions & 0 deletions internal/setup/sdk_init_templates/react-client-sdk.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { asyncWithLDProvider } from 'launchdarkly-react-client-sdk';
// --- init ---
const LDProvider = await asyncWithLDProvider({
clientSideID: '{{.ClientSideID}}',
context: {
kind: 'user',
key: 'example-user-key',
name: 'Example User',
},
});
4 changes: 4 additions & 0 deletions internal/setup/sdk_init_templates/react-native.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { AutoEnvAttributes, ReactNativeLDClient } from '@launchdarkly/react-native-client-sdk';
// --- init ---
const featureClient = new ReactNativeLDClient('{{.MobileKey}}', AutoEnvAttributes.Enabled);
await featureClient.identify({ kind: 'user', key: 'example-user-key', name: 'Example User' });
12 changes: 12 additions & 0 deletions internal/setup/sdk_init_templates/ruby-server-sdk.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
require 'ldclient-rb'
# --- init ---
ld_client = LaunchDarkly::LDClient.new("{{.SDKKey}}")

context = LaunchDarkly::LDContext.create({
key: "example-user-key",
kind: "user",
name: "Example User"
})

flag_value = ld_client.variation("{{.FlagKey}}", context, false)
puts "Flag '{{.FlagKey}}' is #{flag_value}"
6 changes: 6 additions & 0 deletions internal/setup/sdk_init_templates/swift-client-sdk.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import LaunchDarkly
// --- init ---
let ldConfig = LDConfig(mobileKey: "{{.MobileKey}}", autoEnvAttributes: .enabled)
guard case .success(let ldContext) = LDContextBuilder(key: "example-user-key").build()
else { return }
LDClient.start(config: ldConfig, context: ldContext)
Comment thread
cursor[bot] marked this conversation as resolved.
Loading