1: <?php
2:
3: namespace LaravelUi5\Core\Commands;
4:
5: use Illuminate\Filesystem\Filesystem;
6: use Illuminate\Support\Facades\File;
7: use Illuminate\Support\Str;
8: use JsonException;
9: use LaravelUi5\Core\Commands\Concerns\RunsUi5Build;
10: use LaravelUi5\Core\Infrastructure\Contracts\Ui5SourceOverrideStoreInterface;
11: use LaravelUi5\Core\Introspection\Library\Ui5LibrarySource;
12: use LogicException;
13:
14: class GenerateUi5LibraryCommand extends BaseGenerator
15: {
16: use RunsUi5Build;
17:
18: protected $signature = 'ui5:lib
19: {name : The CamelCase name of the library}
20: {--php-ns-prefix=Pragmatiqu : The PHP namespace prefix}
21: {--js-ns-prefix=io.pragmatiqu : The JS namespace prefix}
22: {--create : Create a new module from scratch}
23: {--refresh : Refresh metadata and assets from source project}
24: {--auto-build : Run UI5 build before importing assets}';
25:
26: protected $description = 'Generate a Ui5Library module from a UI5 library project';
27:
28: public function __construct(
29: protected Filesystem $files
30: )
31: {
32: parent::__construct();
33: }
34:
35: /**
36: * @throws JsonException
37: */
38: public function handle(Ui5SourceOverrideStoreInterface $store): int
39: {
40: $name = $this->argument('name');
41: $this->assertCamelCase('Ui5Library', $name);
42:
43: $kebab = Str::kebab($name);
44: $phpPrefix = rtrim($this->option('php-ns-prefix'), '\\');
45: $phpNamespace = "{$phpPrefix}\\{$name}";
46: $jsPrefix = rtrim($this->option('js-ns-prefix'), '.');
47:
48: // Support both LaravelUi5 and Easy UI5 conventions
49: $conventionPaths = [
50: base_path("../ui5-{$kebab}-lib/"),
51: base_path("../{$jsPrefix}.{$kebab}/"),
52: ];
53:
54: /** @var string $sourcePath */
55: $sourcePath = collect($conventionPaths)
56: ->first(fn($path) => File::exists($path));
57:
58: if (is_null($sourcePath)) {
59: throw new LogicException(
60: sprintf(
61: 'Source folder for UI5 app not found. Tried:\n - %s',
62: implode("\n - ", $conventionPaths)
63: )
64: );
65: }
66:
67: if ($this->option('auto-build')) {
68: $this->runBuild($sourcePath);
69: }
70:
71: // Build source model
72: $source = Ui5LibrarySource::fromWorkspace($sourcePath);
73:
74: $targetPath = base_path("ui5/{$name}/src/");
75: $classPath = base_path("ui5/{$name}/src/{$name}Library.php");
76: $modulePath = base_path("ui5/{$name}/src/{$name}Module.php");
77:
78: $create = $this->option('create');
79: $refresh = $this->option('refresh');
80: $exists = File::exists($classPath);
81:
82: // Decision tree
83: if ($create && $exists) {
84: $this->components->error("Module already exists. Use --refresh to update.");
85: return 1;
86: }
87:
88: if ($refresh && !$exists) {
89: $this->components->error("Module does not exist. Use --create to scaffold.");
90: return 1;
91: }
92:
93: if (!$create && !$refresh) {
94: if ($exists) {
95: $this->components->info("Module already exists. Use --refresh to update.");
96: } else {
97: $this->components->info("Module does not exist. Use --create to scaffold.");
98: }
99: return 0;
100: }
101:
102: File::ensureDirectoryExists($targetPath);
103:
104:
105: // Generate Ui5Library class
106: $descriptor = $source->getDescriptor();
107: $this->files->put($classPath, $this->compileStub('Ui5Library.stub', [
108: 'namespace' => $phpNamespace,
109: 'class' => "{$name}Library",
110: 'ui5Namespace' => $descriptor->getNamespace(),
111: 'version' => $descriptor->getVersion(),
112: 'title' => $descriptor->getTitle(),
113: 'description' => $descriptor->getDescription(),
114: 'vendor' => $descriptor->getVendor(),
115: ]));
116:
117: // Generate Ui5Module class
118: $this->files->put($modulePath, $this->compileStub('Ui5ModuleLib.stub', [
119: 'phpNamespace' => $phpNamespace,
120: 'class' => "{$name}Library",
121: 'moduleClass' => "{$name}Module",
122: 'name' => json_encode($name),
123: ]));
124:
125: // Copy artefacts
126: $this->copyLibraryAssets($source, $targetPath);
127:
128: // Register source
129: $store->put("$phpNamespace\\{$name}Module", $sourcePath);
130:
131: $operation = $exists ? 'Updated' : 'Created';
132: $this->components->success("{$operation} Ui5Library module `{$name}`");
133:
134: return self::SUCCESS;
135: }
136:
137: protected function copyLibraryAssets(Ui5LibrarySource $source, string $target): void
138: {
139: $distPath = $source->getSourcePath()
140: . '/dist/resources/'
141: . str_replace('.', '/', $source->getDescriptor()->getNamespace());
142:
143: $staticFiles = [
144: 'manifest.json',
145: 'library-preload.js',
146: 'library-preload.js.map',
147: 'library-dbg.js',
148: 'library-dbg.js.map',
149: ];
150:
151: $i18nFiles = collect(File::files($distPath))
152: ->filter(fn($f) => Str::endsWith($f->getFilename(), '.properties'))
153: ->map(fn($f) => $f->getFilename())
154: ->all();
155:
156: $assets = array_unique(array_merge($staticFiles, $i18nFiles));
157:
158: $this->copyDistAssets($distPath, $target, $assets);
159: }
160: }
161: