1// Copyright 2019 The Hugo Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14// Package modules provides a client that can be used to manage Hugo Components,
15// what's referred to as Hugo Modules. Hugo Modules is built on top of Go Modules,
16// but also supports vendoring and components stored directly in the themes dir.
17package modules
18
19import (
20	"time"
21
22	"github.com/gohugoio/hugo/config"
23)
24
25var _ Module = (*moduleAdapter)(nil)
26
27type Module interface {
28
29	// Optional config read from the configFilename above.
30	Cfg() config.Provider
31
32	// The decoded module config and mounts.
33	Config() Config
34
35	// Optional configuration filenames (e.g. "/themes/mytheme/config.json").
36	// This will be added to the special configuration watch list when in
37	// server mode.
38	ConfigFilenames() []string
39
40	// Directory holding files for this module.
41	Dir() string
42
43	// This module is disabled.
44	Disabled() bool
45
46	// Returns whether this is a Go Module.
47	IsGoMod() bool
48
49	// Any directory remappings.
50	Mounts() []Mount
51
52	// In the dependency tree, this is the first module that defines this module
53	// as a dependency.
54	Owner() Module
55
56	// Returns the path to this module.
57	// This will either be the module path, e.g. "github.com/gohugoio/myshortcodes",
58	// or the path below your /theme folder, e.g. "mytheme".
59	Path() string
60
61	// Replaced by this module.
62	Replace() Module
63
64	// Returns whether Dir points below the _vendor dir.
65	Vendor() bool
66
67	// The module version.
68	Version() string
69
70	// Time version was created.
71	Time() time.Time
72
73	// Whether this module's dir is a watch candidate.
74	Watch() bool
75}
76
77type Modules []Module
78
79type moduleAdapter struct {
80	path       string
81	dir        string
82	version    string
83	vendor     bool
84	disabled   bool
85	projectMod bool
86	owner      Module
87
88	mounts []Mount
89
90	configFilenames []string
91	cfg             config.Provider
92	config          Config
93
94	// Set if a Go module.
95	gomod *goModule
96}
97
98func (m *moduleAdapter) Cfg() config.Provider {
99	return m.cfg
100}
101
102func (m *moduleAdapter) Config() Config {
103	return m.config
104}
105
106func (m *moduleAdapter) ConfigFilenames() []string {
107	return m.configFilenames
108}
109
110func (m *moduleAdapter) Dir() string {
111	// This may point to the _vendor dir.
112	if !m.IsGoMod() || m.dir != "" {
113		return m.dir
114	}
115	return m.gomod.Dir
116}
117
118func (m *moduleAdapter) Disabled() bool {
119	return m.disabled
120}
121
122func (m *moduleAdapter) IsGoMod() bool {
123	return m.gomod != nil
124}
125
126func (m *moduleAdapter) Mounts() []Mount {
127	return m.mounts
128}
129
130func (m *moduleAdapter) Owner() Module {
131	return m.owner
132}
133
134func (m *moduleAdapter) Path() string {
135	if !m.IsGoMod() || m.path != "" {
136		return m.path
137	}
138	return m.gomod.Path
139}
140
141func (m *moduleAdapter) Replace() Module {
142	if m.IsGoMod() && !m.Vendor() && m.gomod.Replace != nil {
143		return &moduleAdapter{
144			gomod: m.gomod.Replace,
145			owner: m.owner,
146		}
147	}
148	return nil
149}
150
151func (m *moduleAdapter) Vendor() bool {
152	return m.vendor
153}
154
155func (m *moduleAdapter) Version() string {
156	if !m.IsGoMod() || m.version != "" {
157		return m.version
158	}
159	return m.gomod.Version
160}
161
162func (m *moduleAdapter) Time() time.Time {
163	if !m.IsGoMod() || m.gomod.Time == nil {
164		return time.Time{}
165	}
166
167	return *m.gomod.Time
168
169}
170
171func (m *moduleAdapter) Watch() bool {
172	if m.Owner() == nil {
173		// Main project
174		return true
175	}
176
177	if !m.IsGoMod() {
178		// Module inside /themes
179		return true
180	}
181
182	if m.Replace() != nil {
183		// Version is not set when replaced by a local folder.
184		return m.Replace().Version() == ""
185	}
186
187	return false
188}
189